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

[NEW] Add bottomSheet for members #2002

Open
wants to merge 6 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 @@ -74,8 +74,8 @@ class ChatDetailsPresenter @Inject constructor(
navigator.toFileList(chatRoomId)
}

fun toMembers(chatRoomId: String) {
navigator.toMembersList(chatRoomId)
fun toMembers(chatRoomId: String, isOwner: Boolean, isMod: Boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

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

isModerator sounds better than isMod. You have even created a function with that name on ChatRoomPresenter.kt for instance.

navigator.toMembersList(chatRoomId, isOwner, isMod)
}

fun toMentions(chatRoomId: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ fun newInstance(
chatRoomId: String,
chatRoomType: String,
isSubscribed: Boolean,
isFavorite: Boolean,
disableMenu: Boolean
disableMenu: Boolean,
Copy link
Contributor

Choose a reason for hiding this comment

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

Well, I think you wrong deleted the isFavorite param here.

isOwner: Boolean,
isMod: Boolean
): ChatDetailsFragment {
return ChatDetailsFragment().apply {
arguments = Bundle(5).apply {
Expand All @@ -45,6 +46,8 @@ fun newInstance(
putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_IS_FAVORITE, isFavorite)
putBoolean(BUNDLE_DISABLE_MENU, disableMenu)
putBoolean(BUNDLE_CHAT_ROOM_OWNER, isOwner)
putBoolean(BUNDLE_CHAT_ROOM_MOD, isMod)
}
}
}
Expand All @@ -54,6 +57,8 @@ internal const val MENU_ACTION_FAVORITE_REMOVE_FAVORITE = 1
internal const val MENU_ACTION_VIDEO_CALL = 2

private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID"
private const val BUNDLE_CHAT_ROOM_MOD = "BUNDLE_CHAT_ROOM_MOD"
private const val BUNDLE_CHAT_ROOM_OWNER = "BUNDLE_CHAT_ROOM_OWNER"
private const val BUNDLE_CHAT_ROOM_TYPE = "BUNDLE_CHAT_ROOM_TYPE"
private const val BUNDLE_IS_SUBSCRIBED = "BUNDLE_IS_SUBSCRIBED"
private const val BUNDLE_IS_FAVORITE = "BUNDLE_IS_FAVORITE"
Expand All @@ -74,22 +79,24 @@ class ChatDetailsFragment : Fragment(), ChatDetailsView {
internal lateinit var chatRoomId: String
internal lateinit var chatRoomType: String
private var isSubscribed: Boolean = true
internal var isFavorite: Boolean = false
private var isOwner: Boolean = false
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is the isFavorite param?

private var isMod: Boolean = false
private var disableMenu: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)

arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)
chatRoomType = getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = getBoolean(BUNDLE_IS_SUBSCRIBED)
isFavorite = getBoolean(BUNDLE_IS_FAVORITE)
disableMenu = getBoolean(BUNDLE_DISABLE_MENU)
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }

setHasOptionsMenu(true)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED)
disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU)
isOwner = bundle.getBoolean(BUNDLE_CHAT_ROOM_OWNER)
isMod = bundle.getBoolean(BUNDLE_CHAT_ROOM_MOD)
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is the isFavorite param? We have a bundle for it.

} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}

override fun onCreateView(
Expand Down Expand Up @@ -166,7 +173,7 @@ class ChatDetailsFragment : Fragment(), ChatDetailsView {
getString(R.string.title_members),
R.drawable.ic_people_outline_black_24dp
) {
presenter.toMembers(chatRoomId!!)
presenter.toMembers(chatRoomId!!, isOwner, isMod)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,25 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
chatRoomId: String,
chatRoomType: String,
isChatRoomSubscribed: Boolean,
isChatRoomFavorite: Boolean,
isMenuDisabled: Boolean
isMenuDisabled: Boolean,
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is the isFavorite param?

isOwner: Boolean,
isModerator: Boolean
) {
activity.addFragmentBackStack(TAG_CHAT_DETAILS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.chatdetails.ui.newInstance(
chatRoomId,
chatRoomType,
isChatRoomSubscribed,
isChatRoomFavorite,
isMenuDisabled
isMenuDisabled,
isOwner,
isModerator
)
}
}

fun toMembersList(chatRoomId: String) {
fun toMembersList(chatRoomId: String, isOwner: Boolean, isMod: Boolean) {
activity.addFragmentBackStack(TAG_MEMBERS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.members.ui.newInstance(chatRoomId)
chat.rocket.android.members.ui.newInstance(chatRoomId, isOwner, isMod)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class ChatRoomPresenter @Inject constructor(
chatRoles = emptyList()
} finally {
// User has at least an 'owner' or 'moderator' role.
val canModerate = isOwnerOrMod()
val canModerate = isOwner() || isModerator()
// Can post anyway if has the 'post-readonly' permission on server.
val room = dbManager.getRoom(roomId)
room?.let {
Expand Down Expand Up @@ -192,9 +192,15 @@ class ChatRoomPresenter @Inject constructor(
chatRoomId?.let { manager.removeRoomChannel(it) }
}

private fun isOwnerOrMod(): Boolean {
return chatRoles.firstOrNull { it.user.username == currentLoggedUsername }?.roles?.any {
it == "owner" || it == "moderator"
private fun isOwner(): Boolean {
return chatRoles.find { it.user.username == currentLoggedUsername }?.roles?.any {
it == "owner"
} ?: false
}

private fun isModerator(): Boolean {
return chatRoles.find { it.user.username == currentLoggedUsername }?.roles?.any {
it == "moderator"
} ?: false
}

Expand Down Expand Up @@ -896,7 +902,7 @@ class ChatRoomPresenter @Inject constructor(
isFavorite: Boolean,
isMenuDisabled: Boolean
) {
navigator.toChatDetails(chatRoomId, chatRoomType, isSubscribed, isFavorite, isMenuDisabled)
navigator.toChatDetails(chatRoomId, chatRoomType, isSubscribed, isMenuDisabled, isOwner(), isModerator())
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is the isFavorite param?

}

fun loadChatRoomsSuggestions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
@Inject
lateinit var analyticsManager: AnalyticsManager
private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter {
it.username?.run { processSelectedMember(this) }
}
private val adapter: MembersAdapter = MembersAdapter ({
if (it.username != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why doing it instead of it.username?.run { processSelectedMember(this) }?

processSelectedMember(it.username)
}
}, null, true, false)
private val compositeDisposable = CompositeDisposable()
private var channelType: String = RoomType.CHANNEL
private var isChannelReadOnly: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package chat.rocket.android.members.adapter

import android.view.View
import android.util.Log
import android.view.MenuItem
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_member.view.*

class MembersAdapter(
private val listener: (MemberUiModel) -> Unit
) : RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
class MembersAdapter(private val listener: (MemberUiModel) -> Unit, presenter: MembersPresenter?, private val isOwner: Boolean, private val isMod: Boolean) :
RecyclerView.Adapter<ViewHolder>() {
private var dataSet: List<MemberUiModel> = ArrayList()
private val enableActions: Boolean = true

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder =
ViewHolder(parent.inflate(R.layout.item_member))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(parent.inflate(R.layout.item_member), actionsListener, isOwner, isMod)
Copy link
Contributor

Choose a reason for hiding this comment

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

isModerator sounds better than isMod. You have even created a function with that name on ChatRoomPresenter.kt for instance.


override fun onBindViewHolder(holder: MembersAdapter.ViewHolder, position: Int) =
holder.bind(dataSet[position], listener)
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
holder.bind(dataSet[position], position, listener)

override fun getItemCount(): Int = dataSet.size

Expand All @@ -39,14 +38,57 @@ class MembersAdapter(
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
}

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

fun bind(memberUiModel: MemberUiModel, listener: (MemberUiModel) -> Unit) = with(itemView) {
image_avatar.setImageURI(memberUiModel.avatarUri)
text_member.content = memberUiModel.displayName
text_member.setCompoundDrawablesRelativeWithIntrinsicBounds(
DrawableHelper.getUserStatusDrawable(memberUiModel.status, context), null, null, null)
setOnClickListener { listener(memberUiModel) }
private val actionsListener = object : ViewHolder.ActionsListener {
override fun isActionsEnabled(): Boolean = enableActions
override fun onActionSelected(item: MenuItem, member: MemberUiModel, index: Int) {
member.apply {
when (item.itemId) {
R.id.action_member_set_owner-> {
val isOwner = this.roles?.contains("owner") == true
presenter?.toggleOwner(this.userId, isOwner) {
if (isOwner)
dataSet[index].roles = dataSet[index].roles?.filterNot { it == "owner"}
else
dataSet[index].roles = dataSet[index].roles?.plus("owner")
notifyItemChanged(index)
}
}
R.id.action_member_set_leader-> {
val isLeader = this.roles?.contains("leader") == true
presenter?.toggleLeader(this.userId, isLeader) {
if (isLeader)
dataSet[index].roles = dataSet[index].roles?.filterNot { it == "leader"}
else
dataSet[index].roles = dataSet[index].roles?.plus("leader")
notifyItemChanged(index)
}
}
R.id.action_member_set_moderator-> {
val isMod = this.roles?.contains("moderator") == true
presenter?.toggleModerator(this.userId, isMod) {
if (isMod)
dataSet[index].roles = dataSet[index].roles?.filterNot { it == "moderator" }
else
dataSet[index].roles = dataSet[index].roles?.plus("moderator")
notifyItemChanged(index)
}
}
R.id.action_member_mute-> {
presenter?.toggleMute(this.username, this.muted) {
dataSet[index].muted = !this.muted
notifyItemChanged(index)
}
}
R.id.action_member_remove-> {
presenter?.removeUser(this.userId) {
dataSet = dataSet.filterIndexed{ position, _-> position != index }
notifyItemRemoved(index)
notifyItemRangeChanged(index, dataSet.size)
}
}
else -> TODO("Not implemented")
}
}
}
}
}
101 changes: 101 additions & 0 deletions app/src/main/java/chat/rocket/android/members/adapter/ViewHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package chat.rocket.android.members.adapter

import android.view.ContextThemeWrapper
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.members.ui.GroupMemberBottomSheet
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.toList
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_member.view.*


class ViewHolder(
itemView: View,
private val listener: ActionsListener,
private val isOwner: Boolean,
private val isMod: Boolean
) : RecyclerView.ViewHolder(itemView), MenuItem.OnMenuItemClickListener {
var data: MemberUiModel? = null
var index: Int = 0

fun bind(memberUiModel: MemberUiModel, position: Int, listener: (MemberUiModel) -> Unit) = with(itemView) {
data = memberUiModel
index = position
image_avatar.setImageURI(memberUiModel.avatarUri)
text_member.content = memberUiModel.displayName
text_member.setCompoundDrawablesRelativeWithIntrinsicBounds(DrawableHelper.getUserStatusDrawable(memberUiModel.status, context), null, null, null)
text_member_owner.setText(R.string.owner)
text_member_leader.setText(R.string.leader)
text_member_moderator.setText(R.string.moderator)
text_member_owner.isVisible =memberUiModel.roles?.contains("owner") == true
text_member_leader.isVisible = memberUiModel.roles?.contains("leader") == true
text_member_moderator.isVisible = memberUiModel.roles?.contains("moderator") == true
setOnClickListener { listener(memberUiModel) }
setupActionMenu(itemView)
}

interface ActionsListener {
fun isActionsEnabled(): Boolean
fun onActionSelected(item: MenuItem, member: MemberUiModel, index: Int)
}

internal fun setupActionMenu(view: View) {
view.setOnLongClickListener{
data?.let {
var menuItems = view.context.inflate(R.menu.group_member_actions).toList()
if (!isOwner && !isMod)
menuItems = menuItems.filter { it.itemId == R.id.action_member_mute }
else if (!isOwner && isMod)
menuItems = menuItems.filter { it.itemId == R.id.action_member_mute || it.itemId == R.id.action_member_remove}
menuItems.find { it.itemId == R.id.action_member_set_owner }?.apply {
if (it.roles?.contains("owner") == true) setTitle(R.string.action_remove_owner)
}
menuItems.find { it.itemId == R.id.action_member_set_leader }?.apply {
if (it.roles?.contains("leader") == true) setTitle(R.string.action_remove_leader)
}
menuItems.find { it.itemId == R.id.action_member_set_moderator }?.apply {
if (it.roles?.contains("moderator") == true) setTitle(R.string.action_remove_moderator)
}
menuItems.find { it.itemId == R.id.action_member_mute }?.apply {
if (it.muted) {
setTitle(R.string.action_unmute_user)
setIcon(R.drawable.ic_mic_on_24dp)
}
}
// TODO: Check why ignore is not working
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you going to check it?

// menuItems.find { it.itemId == R.id.action_member_ignore }?.apply {
// if (it.roles.contains("owner")) title = "Remove Owner"
// }
view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
if (this.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
val actionsBottomSheet = GroupMemberBottomSheet()
actionsBottomSheet.addItems(menuItems, this@ViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
}
}
true
}
}

override fun onMenuItemClick(item: MenuItem): Boolean {
data?.let {
listener.onActionSelected(item, it, index)
}
return true
}
}


Loading