Open room member profile from reactions and read receipts. (#990)

Open room member profile from reactions and read receipts. Fixes #875
This commit is contained in:
Onuray Sahin 2020-02-12 17:57:49 +03:00 committed by GitHub
parent 7c5bb4ff5b
commit 6013e1653b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 49 additions and 6 deletions

View File

@ -6,6 +6,7 @@ Features ✨:
Improvements 🙌: Improvements 🙌:
- Show confirmation dialog before deleting a message (#967) - Show confirmation dialog before deleting a message (#967)
- Open room member profile from reactions list and read receipts list (#875)
Other changes: Other changes:
- -

View File

@ -1045,7 +1045,7 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onAvatarClicked(informationData: MessageInformationData) { override fun onAvatarClicked(informationData: MessageInformationData) {
// roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.senderId)) // roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.userId))
openRoomMemberProfile(informationData.senderId) openRoomMemberProfile(informationData.senderId)
} }
@ -1106,7 +1106,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleActions(action: EventSharedAction) { private fun handleActions(action: EventSharedAction) {
when (action) { when (action) {
is EventSharedAction.OpenUserProfile -> { is EventSharedAction.OpenUserProfile -> {
openRoomMemberProfile(action.senderId) openRoomMemberProfile(action.userId)
} }
is EventSharedAction.AddReaction -> { is EventSharedAction.AddReaction -> {
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE)

View File

@ -33,6 +33,7 @@ abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptI
@EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var timestamp: CharSequence? = null @EpoxyAttribute var timestamp: CharSequence? = null
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var userClicked: (() -> Unit)? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
avatarRenderer.render(matrixItem, holder.avatarView) avatarRenderer.render(matrixItem, holder.avatarView)
@ -43,6 +44,7 @@ abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptI
} ?: run { } ?: run {
holder.timestampView.isVisible = false holder.timestampView.isVisible = false
} }
holder.view.setOnClickListener { userClicked?.invoke() }
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View File

@ -27,6 +27,8 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedAction
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.* import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
@ -40,7 +42,7 @@ data class DisplayReadReceiptArgs(
/** /**
* Bottom sheet displaying list of read receipts for a given event ordered by descending timestamp * Bottom sheet displaying list of read receipts for a given event ordered by descending timestamp
*/ */
class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() { class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), DisplayReadReceiptsController.Listener {
@Inject lateinit var epoxyController: DisplayReadReceiptsController @Inject lateinit var epoxyController: DisplayReadReceiptsController
@ -49,6 +51,8 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
private val displayReadReceiptArgs: DisplayReadReceiptArgs by args() private val displayReadReceiptArgs: DisplayReadReceiptArgs by args()
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
} }
@ -57,16 +61,23 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
recyclerView.configureWith(epoxyController, hasFixedSize = false) recyclerView.configureWith(epoxyController, hasFixedSize = false)
bottomSheetTitle.text = getString(R.string.seen_by) bottomSheetTitle.text = getString(R.string.seen_by)
epoxyController.listener = this
epoxyController.setData(displayReadReceiptArgs.readReceipts) epoxyController.setData(displayReadReceiptArgs.readReceipts)
} }
override fun onDestroyView() { override fun onDestroyView() {
recyclerView.cleanup() recyclerView.cleanup()
epoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }
override fun didSelectUser(userId: String) {
sharedActionViewModel.post(EventSharedAction.OpenUserProfile(userId))
}
// we are not using state for this one as it's static, so no need to override invalidate() // we are not using state for this one as it's static, so no need to override invalidate()
companion object { companion object {

View File

@ -32,6 +32,8 @@ class DisplayReadReceiptsController @Inject constructor(private val dateFormatte
private val avatarRender: AvatarRenderer) private val avatarRender: AvatarRenderer)
: TypedEpoxyController<List<ReadReceiptData>>() { : TypedEpoxyController<List<ReadReceiptData>>() {
var listener: Listener? = null
override fun buildModels(readReceipts: List<ReadReceiptData>) { override fun buildModels(readReceipts: List<ReadReceiptData>) {
readReceipts.forEach { readReceipts.forEach {
val timestamp = dateFormatter.formatRelativeDateTime(it.timestamp) val timestamp = dateFormatter.formatRelativeDateTime(it.timestamp)
@ -40,7 +42,12 @@ class DisplayReadReceiptsController @Inject constructor(private val dateFormatte
.matrixItem(it.toMatrixItem()) .matrixItem(it.toMatrixItem())
.avatarRenderer(avatarRender) .avatarRenderer(avatarRender)
.timestamp(timestamp) .timestamp(timestamp)
.userClicked { listener?.didSelectUser(it.userId) }
.addIf(session.myUserId != it.userId, this) .addIf(session.myUserId != it.userId, this)
} }
} }
interface Listener {
fun didSelectUser(userId: String)
}
} }

View File

@ -28,7 +28,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int,
object Separator : object Separator :
EventSharedAction(0, 0) EventSharedAction(0, 0)
data class OpenUserProfile(val senderId: String) : data class OpenUserProfile(val userId: String) :
EventSharedAction(0, 0) EventSharedAction(0, 0)
data class AddReaction(val eventId: String) : data class AddReaction(val eventId: String) :

View File

@ -36,6 +36,8 @@ abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleI
lateinit var authorDisplayName: CharSequence lateinit var authorDisplayName: CharSequence
@EpoxyAttribute @EpoxyAttribute
var timeStamp: CharSequence? = null var timeStamp: CharSequence? = null
@EpoxyAttribute
var userClicked: (() -> Unit)? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.emojiReactionView.text = reactionKey holder.emojiReactionView.text = reactionKey
@ -46,6 +48,7 @@ abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleI
} ?: run { } ?: run {
holder.timeStampView.isVisible = false holder.timeStampView.isVisible = false
} }
holder.view.setOnClickListener { userClicked?.invoke() }
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View File

@ -27,6 +27,8 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.riotx.features.home.room.detail.timeline.action.EventSharedAction
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.* import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
@ -35,11 +37,12 @@ import javax.inject.Inject
/** /**
* Bottom sheet displaying list of reactions for a given event ordered by timestamp * Bottom sheet displaying list of reactions for a given event ordered by timestamp
*/ */
class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() { class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReactionsEpoxyController.Listener {
private val viewModel: ViewReactionsViewModel by fragmentViewModel(ViewReactionsViewModel::class) private val viewModel: ViewReactionsViewModel by fragmentViewModel(ViewReactionsViewModel::class)
@Inject lateinit var viewReactionsViewModelFactory: ViewReactionsViewModel.Factory @Inject lateinit var viewReactionsViewModelFactory: ViewReactionsViewModel.Factory
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
@BindView(R.id.bottomSheetRecyclerView) @BindView(R.id.bottomSheetRecyclerView)
lateinit var recyclerView: RecyclerView lateinit var recyclerView: RecyclerView
@ -54,15 +57,22 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
recyclerView.configureWith(epoxyController, hasFixedSize = false) recyclerView.configureWith(epoxyController, hasFixedSize = false)
bottomSheetTitle.text = context?.getString(R.string.reactions) bottomSheetTitle.text = context?.getString(R.string.reactions)
epoxyController.listener = this
} }
override fun onDestroyView() { override fun onDestroyView() {
recyclerView.cleanup() recyclerView.cleanup()
epoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }
override fun didSelectUser(userId: String) {
sharedActionViewModel.post(EventSharedAction.OpenUserProfile(userId))
}
override fun invalidate() = withState(viewModel) { override fun invalidate() = withState(viewModel) {
epoxyController.setData(it) epoxyController.setData(it)
super.invalidate() super.invalidate()

View File

@ -35,6 +35,8 @@ class ViewReactionsEpoxyController @Inject constructor(
private val emojiCompatWrapper: EmojiCompatWrapper) private val emojiCompatWrapper: EmojiCompatWrapper)
: TypedEpoxyController<DisplayReactionsViewState>() { : TypedEpoxyController<DisplayReactionsViewState>() {
var listener: Listener? = null
override fun buildModels(state: DisplayReactionsViewState) { override fun buildModels(state: DisplayReactionsViewState) {
when (state.mapReactionKeyToMemberList) { when (state.mapReactionKeyToMemberList) {
is Incomplete -> { is Incomplete -> {
@ -55,9 +57,14 @@ class ViewReactionsEpoxyController @Inject constructor(
timeStamp(it.timestamp) timeStamp(it.timestamp)
reactionKey(emojiCompatWrapper.safeEmojiSpanify(it.reactionKey)) reactionKey(emojiCompatWrapper.safeEmojiSpanify(it.reactionKey))
authorDisplayName(it.authorName ?: it.authorId) authorDisplayName(it.authorName ?: it.authorId)
userClicked { listener?.didSelectUser(it.authorId) }
} }
} }
} }
} }
} }
interface Listener {
fun didSelectUser(userId: String)
}
} }

View File

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="40dp" android:minHeight="40dp"
android:orientation="horizontal" android:orientation="horizontal"

View File

@ -3,10 +3,11 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="40dp"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="8dp" android:paddingStart="8dp"
android:minHeight="40dp"
android:paddingEnd="8dp"> android:paddingEnd="8dp">
<TextView <TextView