Skip to content
This repository was archived by the owner on Jun 7, 2020. It is now read-only.

Commit 56bc23c

Browse files
committed
add bottomsheet and favorite functionality
1 parent 6f2f5c8 commit 56bc23c

15 files changed

Lines changed: 273 additions & 16 deletions

app/src/main/java/chat/rocket/android/chatdetails/adapter/OptionViewHolder.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ import DrawableHelper
44
import android.view.View
55
import android.widget.ImageView
66
import android.widget.TextView
7+
import androidx.recyclerview.widget.RecyclerView
78
import chat.rocket.android.R
89
import chat.rocket.android.chatdetails.domain.Option
9-
import chat.rocket.android.chatrooms.adapter.ViewHolder
1010
import kotlinx.android.synthetic.main.item_detail_option.view.*
1111

1212
class OptionViewHolder(
1313
itemView: View
14-
): ViewHolder<OptionItemHolder>(itemView) {
14+
): RecyclerView.ViewHolder(itemView) {
15+
var data: OptionItemHolder? = null
1516

16-
override fun bindViews(data: OptionItemHolder) {
17+
fun bind(data: OptionItemHolder) {
18+
this.data = data
19+
this.bindViews(data)
20+
}
21+
22+
fun bindViews(data: OptionItemHolder) {
1723
val option = data.data
1824
bindName(option, itemView.name)
1925
bindIcon(option, itemView.icon)

app/src/main/java/chat/rocket/android/chatrooms/adapter/HeaderViewHolder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package chat.rocket.android.chatrooms.adapter
33
import android.view.View
44
import kotlinx.android.synthetic.main.item_chatroom_header.view.*
55

6-
class HeaderViewHolder(itemView: View) : ViewHolder<HeaderItemHolder>(itemView) {
6+
class HeaderViewHolder(itemView: View, listener: ActionsListener) : ViewHolder<HeaderItemHolder>(itemView, listener) {
77
override fun bindViews(data: HeaderItemHolder) {
88
with(itemView) {
99
text_chatroom_header.text = data.data

app/src/main/java/chat/rocket/android/chatrooms/adapter/LoadingViewHolder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package chat.rocket.android.chatrooms.adapter
22

33
import android.view.View
44

5-
class LoadingViewHolder(itemView: View) : ViewHolder<ItemHolder<Unit>>(itemView) {
5+
class LoadingViewHolder(itemView: View, listener: ActionsListener) : ViewHolder<ItemHolder<Unit>>(itemView, listener) {
66
override fun bindViews(data: ItemHolder<Unit>) {
77
}
88
}

app/src/main/java/chat/rocket/android/chatrooms/adapter/RoomViewHolder.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ import chat.rocket.common.model.UserStatus
1414
import kotlinx.android.synthetic.main.item_chat.view.*
1515
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
1616

17-
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) :
18-
ViewHolder<RoomItemHolder>(itemView) {
17+
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit, actionsListener: ActionsListener) :
18+
ViewHolder<RoomItemHolder>(itemView, actionsListener) {
19+
20+
init {
21+
with(itemView) {
22+
setupActionMenu(itemView)
23+
}
24+
}
1925
private val resources: Resources = itemView.resources
2026
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp, null)
2127
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp, null)

app/src/main/java/chat/rocket/android/chatrooms/adapter/RoomsAdapter.kt

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package chat.rocket.android.chatrooms.adapter
22

3+
import android.view.MenuItem
34
import android.view.ViewGroup
45
import androidx.recyclerview.widget.RecyclerView
56
import chat.rocket.android.R
7+
import chat.rocket.android.chatroom.uimodel.toViewType
68
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
9+
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
710
import chat.rocket.android.util.extensions.inflate
811

9-
class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
12+
class RoomsAdapter(private val listener: (RoomUiModel) -> Unit, presenter: ChatRoomsPresenter) :
1013
RecyclerView.Adapter<ViewHolder<*>>() {
14+
private val enableActions: Boolean = true
1115

12-
init {
16+
17+
init {
1318
setHasStableIds(true)
1419
}
1520

@@ -22,15 +27,15 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
2227
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> = when (viewType) {
2328
VIEW_TYPE_ROOM -> {
2429
val view = parent.inflate(R.layout.item_chat)
25-
RoomViewHolder(view, listener)
30+
RoomViewHolder(view, listener, actionsListener)
2631
}
2732
VIEW_TYPE_HEADER -> {
2833
val view = parent.inflate(R.layout.item_chatroom_header)
29-
HeaderViewHolder(view)
34+
HeaderViewHolder(view, actionsListener)
3035
}
3136
VIEW_TYPE_LOADING -> {
3237
val view = parent.inflate(R.layout.item_loading)
33-
LoadingViewHolder(view)
38+
LoadingViewHolder(view, actionsListener)
3439
}
3540
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
3641
}
@@ -67,4 +72,27 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
6772
const val VIEW_TYPE_HEADER = 2
6873
const val VIEW_TYPE_LOADING = 3
6974
}
75+
76+
private val actionsListener = object : ViewHolder.ActionsListener {
77+
override fun isActionsEnabled(): Boolean = enableActions
78+
override fun onActionSelected(item: MenuItem, room: RoomUiModel) {
79+
room.apply {
80+
when (item.itemId) {
81+
R.id.action_favorite_room-> {
82+
presenter.toggleFavoriteChatRoom(this.id, this.favorite==true)
83+
}
84+
R.id.action_leave_room-> {
85+
86+
}
87+
R.id.action_read->{
88+
89+
}
90+
R.id.action_hide_room->{
91+
92+
}
93+
else -> TODO("Not implemented")
94+
}
95+
}
96+
}
97+
}
7098
}
Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,79 @@
11
package chat.rocket.android.chatrooms.adapter
22

3+
import android.content.Context
4+
import android.content.ContextWrapper
5+
import android.util.Log
6+
import android.view.ContextThemeWrapper
7+
import android.view.MenuItem
38
import android.view.View
9+
import android.view.ViewGroup
10+
import androidx.appcompat.app.AppCompatActivity
11+
import androidx.core.view.children
12+
import androidx.lifecycle.Lifecycle
413
import androidx.recyclerview.widget.RecyclerView
14+
import chat.rocket.android.R
15+
import chat.rocket.android.chatroom.ui.bottomsheet.ChatRoomActionBottomSheet
16+
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
17+
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
18+
import chat.rocket.android.util.extensions.inflate
19+
import chat.rocket.android.util.extensions.toList
20+
import chat.rocket.core.model.isSystemMessage
521

622
abstract class ViewHolder<T : ItemHolder<*>>(
7-
itemView: View
8-
) : RecyclerView.ViewHolder(itemView) {
23+
itemView: View,
24+
private val listener: ActionsListener
25+
) : RecyclerView.ViewHolder(itemView),
26+
MenuItem.OnMenuItemClickListener {
927
var data: T? = null
1028

29+
init {
30+
setupActionMenu(itemView)
31+
}
32+
1133
fun bind(data: T) {
1234
this.data = data
1335
bindViews(data)
1436
}
1537

1638
abstract fun bindViews(data: T)
39+
40+
interface ActionsListener {
41+
fun isActionsEnabled(): Boolean
42+
fun onActionSelected(item: MenuItem, room: RoomUiModel)
43+
}
44+
45+
46+
internal fun setupActionMenu(view: View) {
47+
view.setOnLongClickListener{
48+
if (data?.data is RoomUiModel) {
49+
data?.let { vm ->
50+
vm.data.let {
51+
val menuItems = view.context.inflate(R.menu.chatrooms_action).toList()
52+
menuItems.find { it.itemId == R.id.action_favorite_room }?.apply {
53+
setTitle(if ((it as RoomUiModel).favorite == true) R.string.title_unfavorite_chat else R.string.title_favorite_chat)
54+
setIcon(if (it.favorite == true) R.drawable.ic_star_yellow_24dp else R.drawable.ic_star_border_black_24dp)
55+
isChecked = (it.favorite==true)
56+
}
57+
view.context?.let {
58+
if (it is ContextThemeWrapper && it is AppCompatActivity) {
59+
with(it) {
60+
val actionsBottomSheet = ChatRoomActionBottomSheet()
61+
actionsBottomSheet.addItems(menuItems, this@ViewHolder)
62+
actionsBottomSheet.show(supportFragmentManager, null)
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
true
70+
}
71+
}
72+
73+
override fun onMenuItemClick(item: MenuItem): Boolean {
74+
data?.let {
75+
listener.onActionSelected(item, (it as RoomItemHolder).data)
76+
}
77+
return true
78+
}
1779
}

app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import chat.rocket.common.RocketChatException
2020
import chat.rocket.common.model.RoomType
2121
import chat.rocket.common.model.User
2222
import chat.rocket.common.model.roomTypeOf
23+
import chat.rocket.common.util.ifNull
2324
import chat.rocket.core.internal.realtime.createDirectMessage
25+
import chat.rocket.core.internal.rest.favorite
2426
import chat.rocket.core.internal.rest.me
2527
import chat.rocket.core.internal.rest.show
2628
import kotlinx.coroutines.withTimeout
@@ -144,6 +146,25 @@ class ChatRoomsPresenter @Inject constructor(
144146
}
145147
}
146148

149+
fun toggleFavoriteChatRoom(roomId: String, isFavorite: Boolean) {
150+
launchUI(strategy) {
151+
try {
152+
// Note that if it is favorite then the user wants to unfavorite - and vice versa.
153+
retryIO("favorite($roomId, $isFavorite)") {
154+
client.favorite(roomId, !isFavorite)
155+
}
156+
// view.showFavoriteIcon(!isFavorite)
157+
} catch (e: RocketChatException) {
158+
Timber.e(e, "Error while trying to favorite/unfavorite chat room.")
159+
e.message?.let {
160+
view.showMessage(it)
161+
}.ifNull {
162+
view.showGenericErrorMessage()
163+
}
164+
}
165+
}
166+
}
167+
147168
private suspend fun getCurrentUser(): User? {
148169
userHelper.user()?.let {
149170
return it

app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsView.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ interface ChatRoomsView : LoadingView, MessageView {
77
fun showLoadingRoom(name: CharSequence)
88

99
fun hideLoadingRoom()
10+
11+
// fun showFavoriteIcon(isFavorite:Boolean)
12+
1013
}

app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
6060
lateinit var analyticsManager: AnalyticsManager
6161

6262
private lateinit var viewModel: ChatRoomsViewModel
63+
private lateinit var adapter: RoomsAdapter
6364

6465
private var searchView: SearchView? = null
6566
private var sortView: MenuItem? = null
@@ -110,11 +111,12 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
110111
analyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
111112
}
112113

114+
113115
private fun subscribeUi() {
114116
ui {
115-
val adapter = RoomsAdapter { room ->
117+
adapter = RoomsAdapter ({ room ->
116118
presenter.loadChatRoom(room)
117-
}
119+
},presenter)
118120

119121
recycler_view.layoutManager = LinearLayoutManager(it)
120122
recycler_view.addItemDecoration(
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package chat.rocket.android.chatroom.ui.bottomsheet
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.MenuItem
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import androidx.recyclerview.widget.LinearLayoutManager
9+
import androidx.recyclerview.widget.RecyclerView
10+
import chat.rocket.android.R
11+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
12+
import kotlinx.android.synthetic.main.message_action_item.view.*
13+
import kotlinx.android.synthetic.main.message_bottomsheet.*
14+
15+
class ChatRoomActionBottomSheet : BottomSheetDialogFragment() {
16+
17+
private var adapter = ChatRoomActionAdapter()
18+
19+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
20+
return inflater.inflate(R.layout.message_bottomsheet, container, false)
21+
}
22+
23+
fun addItems(items: List<MenuItem>, itemClickListener: MenuItem.OnMenuItemClickListener) {
24+
adapter.addItems(items, ActionItemClickListener(dismissAction = { dismiss() },
25+
itemClickListener = itemClickListener))
26+
}
27+
28+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
29+
bottomsheet_recycler_view.layoutManager = LinearLayoutManager(context)
30+
bottomsheet_recycler_view.adapter = adapter
31+
}
32+
33+
private class ActionItemClickListener(
34+
val dismissAction: () -> Unit,
35+
val itemClickListener: MenuItem.OnMenuItemClickListener
36+
)
37+
38+
private class ChatRoomActionAdapter : RecyclerView.Adapter<ChatRoomActionViewHolder>() {
39+
40+
private lateinit var itemClickListener: ActionItemClickListener
41+
private val menuItems = mutableListOf<MenuItem>()
42+
43+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatRoomActionViewHolder {
44+
return ChatRoomActionViewHolder(
45+
LayoutInflater.from(parent.context).inflate(R.layout.message_action_item, parent, false)
46+
)
47+
}
48+
49+
override fun getItemCount() = menuItems.size
50+
51+
override fun onBindViewHolder(holder: ChatRoomActionViewHolder, position: Int) {
52+
holder.bind(menuItems[position], itemClickListener)
53+
}
54+
55+
fun addItems(items: List<MenuItem>, itemClickListener: ActionItemClickListener) {
56+
this.itemClickListener = itemClickListener
57+
menuItems.clear()
58+
menuItems.addAll(items)
59+
notifyDataSetChanged()
60+
}
61+
}
62+
63+
private class ChatRoomActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
64+
65+
fun bind(item: MenuItem, itemClickListener: ActionItemClickListener) {
66+
with(itemView) {
67+
message_action_title.text = item.title
68+
message_action_icon.setImageDrawable(item.icon)
69+
setOnClickListener {
70+
itemClickListener.itemClickListener.onMenuItemClick(item)
71+
itemClickListener.dismissAction.invoke()
72+
}
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)