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

[NEW] Add bottomSheet in Chatrooms #1929

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ import DrawableHelper
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatdetails.domain.Option
import chat.rocket.android.chatrooms.adapter.ViewHolder
import kotlinx.android.synthetic.main.item_detail_option.view.*

class OptionViewHolder(
itemView: View
): ViewHolder<OptionItemHolder>(itemView) {
): RecyclerView.ViewHolder(itemView) {
var data: OptionItemHolder? = null

override fun bindViews(data: OptionItemHolder) {
fun bind(data: OptionItemHolder) {
this.data = data
this.bindViews(data)
}

fun bindViews(data: OptionItemHolder) {
val option = data.data
bindName(option, itemView.name)
bindIcon(option, itemView.icon)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package chat.rocket.android.chatrooms.adapter
import android.view.View
import kotlinx.android.synthetic.main.item_chatroom_header.view.*

class HeaderViewHolder(itemView: View) : ViewHolder<HeaderItemHolder>(itemView) {
class HeaderViewHolder(itemView: View, listener: ActionsListener) : ViewHolder<HeaderItemHolder>(itemView, listener) {
override fun bindViews(data: HeaderItemHolder) {
with(itemView) {
text_chatroom_header.text = data.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package chat.rocket.android.chatrooms.adapter

import android.view.View

class LoadingViewHolder(itemView: View) : ViewHolder<ItemHolder<Unit>>(itemView) {
class LoadingViewHolder(itemView: View, listener: ActionsListener) : ViewHolder<ItemHolder<Unit>>(itemView, listener) {
override fun bindViews(data: ItemHolder<Unit>) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*

class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) :
ViewHolder<RoomItemHolder>(itemView) {
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit, actionsListener: ActionsListener) :
ViewHolder<RoomItemHolder>(itemView, actionsListener) {

private val resources: Resources = itemView.resources
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp, null)
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp, null)
Expand All @@ -24,6 +25,12 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
private val busyIcon: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp, null)
private val offlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp, null)

init {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a better coding style this initializer block comes after the property declaration.
https://kotlinlang.org/docs/reference/coding-conventions.html

with(itemView) {
setupActionMenu(itemView)
}
}

override fun bindViews(data: RoomItemHolder) {
val room = data.data
with(itemView) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package chat.rocket.android.chatrooms.adapter

import android.view.MenuItem
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.util.extensions.inflate

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

init {
init {
setHasStableIds(true)
}

Expand All @@ -22,15 +25,15 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> = when (viewType) {
VIEW_TYPE_ROOM -> {
val view = parent.inflate(R.layout.item_chat)
RoomViewHolder(view, listener)
RoomViewHolder(view, listener, actionsListener)
}
VIEW_TYPE_HEADER -> {
val view = parent.inflate(R.layout.item_chatroom_header)
HeaderViewHolder(view)
HeaderViewHolder(view, actionsListener)
}
VIEW_TYPE_LOADING -> {
val view = parent.inflate(R.layout.item_loading)
LoadingViewHolder(view)
LoadingViewHolder(view, actionsListener)
}
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
}
Expand Down Expand Up @@ -67,4 +70,27 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
const val VIEW_TYPE_HEADER = 2
const val VIEW_TYPE_LOADING = 3
}

private val actionsListener = object : ViewHolder.ActionsListener {
override fun isActionsEnabled(): Boolean = enableActions
override fun onActionSelected(item: MenuItem, room: RoomUiModel) {
room.apply {
when (item.itemId) {
R.id.action_favorite_room-> {
presenter.toggleFavoriteChatRoom(this.id, this.favorite==true)
}
R.id.action_leave_room-> {
presenter.leaveChatRoom(this.id, this.type)
}
R.id.action_read->{
presenter.toggleMarkRead(this.id, this.unread)
}
R.id.action_hide_room->{
presenter.hideRoom(this.id, this.type)
}
else -> TODO("Not implemented")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,73 @@
package chat.rocket.android.chatrooms.adapter

import android.view.ContextThemeWrapper
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.ChatRoomActionBottomSheet
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.toList

abstract class ViewHolder<T : ItemHolder<*>>(
itemView: View
) : RecyclerView.ViewHolder(itemView) {
itemView: View,
private val listener: ActionsListener
) : RecyclerView.ViewHolder(itemView),
MenuItem.OnMenuItemClickListener {
var data: T? = null

init {
setupActionMenu(itemView)
}

fun bind(data: T) {
this.data = data
bindViews(data)
}

abstract fun bindViews(data: T)

interface ActionsListener {
fun isActionsEnabled(): Boolean
fun onActionSelected(item: MenuItem, room: RoomUiModel)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this empty line.

internal fun setupActionMenu(view: View) {
view.setOnLongClickListener{
if (data?.data is RoomUiModel) {
data?.let { vm ->
vm.data.let {
val menuItems = view.context.inflate(R.menu.chatrooms_action).toList()
menuItems.find { it.itemId == R.id.action_favorite_room }?.apply {
setTitle(if ((it as RoomUiModel).favorite == true) R.string.action_unfavorite else R.string.action_favorite)
setIcon(if (it.favorite == true) R.drawable.ic_star_24dp else R.drawable.ic_star_border_24dp)
isChecked = (it.favorite==true)
}
menuItems.find { it.itemId == R.id.action_read }?.apply {
setTitle(if ((it as RoomUiModel).unread.isNullOrEmpty()) R.string.action_mark_unread else R.string.action_mark_read)
}
view.context?.let {
if (it is ContextThemeWrapper && it is AppCompatActivity) {
with(it) {
val actionsBottomSheet = ChatRoomActionBottomSheet()
actionsBottomSheet.addItems(menuItems, this@ViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
}
}
}
true
}
}

override fun onMenuItemClick(item: MenuItem): Boolean {
data?.let {
listener.onActionSelected(item, (it as RoomItemHolder).data)
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.User
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.realtime.createDirectMessage
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.show
import chat.rocket.core.internal.rest.*
import kotlinx.coroutines.withTimeout
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine


class ChatRoomsPresenter @Inject constructor(
private val view: ChatRoomsView,
private val strategy: CancelStrategy,
Expand Down Expand Up @@ -144,6 +145,85 @@ class ChatRoomsPresenter @Inject constructor(
}
}

fun toggleFavoriteChatRoom(roomId: String, isFavorite: Boolean) {
launchUI(strategy) {
try {
// Note that if it is favorite then the user wants to unfavorite - and vice versa.
retryIO("favorite($roomId, $isFavorite)") {
client.favorite(roomId, !isFavorite)
val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update
}
Timber.d("Refreshing rooms: $rooms")
dbManager.processRooms(rooms)
}
} catch (e: RocketChatException) {
Timber.e(e, "Error while trying to favorite/unfavorite chat room.")
e.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
}
}

fun toggleMarkRead(roomId: String, unread: String?) {
launchUI(strategy) {
try {
if(unread.isNullOrEmpty()) {
retryIO(description = "markAsUnread($roomId)") { client.markAsUnread(roomId) }
val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update
}
Timber.d("Refreshing rooms: $rooms")
dbManager.processRooms(rooms)
}
else
retryIO(description = "markAsRead($roomId)") { client.markAsRead(roomId) }
} catch (ex: RocketChatException) {
view.showMessage(ex.message!!) // TODO Remove.
Timber.e(ex) // FIXME: Right now we are only catching the exception with Timber.
}
}
}

fun leaveChatRoom(roomId: String, roomType: RoomType) {
launchUI(strategy) {
try {
retryIO(description = "leaveChat($roomId, $roomType)") { client.leaveChat(roomId, roomType) }
val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update
}
Timber.d("Refreshing rooms: $rooms")
dbManager.processRooms(rooms)
} catch (ex: RocketChatException) {
view.showMessage(ex.message!!) // TODO Remove.
Timber.e(ex) // FIXME: Right now we are only catching the exception with Timber.
}
}
}

fun hideRoom(roomId: String, roomType: RoomType) {
launchUI(strategy) {
try {
retryIO(description = "hide($roomId, $roomType, ${true})") { client.hide(roomId, roomType, hideRoom = true) }
val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update
}
Timber.d("Refreshing rooms: $rooms")
dbManager.processRooms(rooms)
} catch (ex: RocketChatException) {
view.showMessage(ex.message!!) // TODO Remove.
Timber.e(ex) // FIXME: Right now we are only catching the exception with Timber.
}
}
}

private suspend fun getCurrentUser(): User? {
userHelper.user()?.let {
return it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
lateinit var analyticsManager: AnalyticsManager

private lateinit var viewModel: ChatRoomsViewModel
private lateinit var adapter: RoomsAdapter

private var searchView: SearchView? = null
private var sortView: MenuItem? = null
Expand Down Expand Up @@ -110,11 +111,12 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
analyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
}


private fun subscribeUi() {
ui {
val adapter = RoomsAdapter { room ->
adapter = RoomsAdapter ({ room ->
presenter.loadChatRoom(room)
}
},presenter)

recycler_view.layoutManager = LinearLayoutManager(it)
recycler_view.addItemDecoration(
Expand Down
Loading