mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge pull request #2212 from vector-im/feature/bma/jump_to_read_receipt
Add "jump to read receipt" and "mention" actions from room member detail screen
This commit is contained in:
commit
50cf5b5322
15 changed files with 132 additions and 19 deletions
|
@ -15,6 +15,7 @@ Improvements 🙌:
|
|||
- Add a menu item in the timeline as a shortcut to invite user (#2171)
|
||||
- Drawer: move settings access and add sign out action (#2171)
|
||||
- Filter room member (and banned users) by name (#2184)
|
||||
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
||||
|
||||
Bugfix 🐛:
|
||||
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
||||
|
|
|
@ -63,6 +63,14 @@ interface ReadService {
|
|||
*/
|
||||
fun getMyReadReceiptLive(): LiveData<Optional<String>>
|
||||
|
||||
/**
|
||||
* Get the eventId where the read receipt for the provided user is
|
||||
* @param userId the id of the user to look for
|
||||
*
|
||||
* @return the eventId where the read receipt for the provided user is attached, or null if not found
|
||||
*/
|
||||
fun getUserReadReceipt(userId: String): String?
|
||||
|
||||
/**
|
||||
* Returns a live list of read receipts for a given event
|
||||
* @param eventId: the event
|
||||
|
|
|
@ -107,6 +107,16 @@ internal class DefaultReadService @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getUserReadReceipt(userId: String): String? {
|
||||
var eventId: String? = null
|
||||
monarchy.doWithRealm {
|
||||
eventId = ReadReceiptEntity.where(it, roomId = roomId, userId = userId)
|
||||
.findFirst()
|
||||
?.eventId
|
||||
}
|
||||
return eventId
|
||||
}
|
||||
|
||||
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
||||
val liveRealmData = monarchy.findAllMappedWithChanges(
|
||||
{ ReadReceiptsSummaryEntity.where(it, eventId) },
|
||||
|
|
|
@ -36,6 +36,7 @@ import im.vector.app.features.crypto.verification.IncomingVerificationRequestHan
|
|||
import im.vector.app.features.grouplist.SelectedGroupDataSource
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.HomeRoomListDataSource
|
||||
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.html.EventHtmlRenderer
|
||||
import im.vector.app.features.html.VectorHtmlCompressor
|
||||
|
@ -114,6 +115,8 @@ interface VectorComponent {
|
|||
|
||||
fun selectedGroupStore(): SelectedGroupDataSource
|
||||
|
||||
fun roomDetailPendingActionStore(): RoomDetailPendingActionStore
|
||||
|
||||
fun activeSessionObservableStore(): ActiveSessionDataSource
|
||||
|
||||
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
|
||||
|
|
|
@ -23,6 +23,7 @@ const val THREE_MINUTES = 3 * 60_000L
|
|||
|
||||
/**
|
||||
* Store an object T for a specific period of time
|
||||
* @param delay delay to keep the data, in millis
|
||||
*/
|
||||
open class TemporaryStore<T>(private val delay: Long = THREE_MINUTES) {
|
||||
|
||||
|
@ -30,14 +31,16 @@ open class TemporaryStore<T>(private val delay: Long = THREE_MINUTES) {
|
|||
|
||||
var data: T? = null
|
||||
set(value) {
|
||||
field = value
|
||||
timer?.cancel()
|
||||
timer = Timer().also {
|
||||
it.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
field = null
|
||||
}
|
||||
}, delay)
|
||||
field = value
|
||||
if (value != null) {
|
||||
timer = Timer().also {
|
||||
it.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
field = null
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,4 +88,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||
data class EnsureNativeWidgetAllowed(val widget: Widget,
|
||||
val userJustAccepted: Boolean,
|
||||
val grantedEvents: RoomDetailViewEvents) : RoomDetailAction()
|
||||
|
||||
data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
|
||||
}
|
||||
|
|
|
@ -216,7 +216,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
private val notificationUtils: NotificationUtils,
|
||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||
private val imageContentRenderer: ImageContentRenderer
|
||||
private val imageContentRenderer: ImageContentRenderer,
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||
) :
|
||||
VectorBaseFragment(),
|
||||
TimelineEventController.Callback,
|
||||
|
@ -878,6 +879,17 @@ class RoomDetailFragment @Inject constructor(
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId)
|
||||
roomDetailPendingActionStore.data?.let { handlePendingAction(it) }
|
||||
roomDetailPendingActionStore.data = null
|
||||
}
|
||||
|
||||
private fun handlePendingAction(roomDetailPendingAction: RoomDetailPendingAction) {
|
||||
when (roomDetailPendingAction) {
|
||||
is RoomDetailPendingAction.JumpToReadReceipt ->
|
||||
roomDetailViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
|
||||
is RoomDetailPendingAction.MentionUser ->
|
||||
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.detail
|
||||
|
||||
sealed class RoomDetailPendingAction {
|
||||
data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction()
|
||||
data class MentionUser(val userId: String) : RoomDetailPendingAction()
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.detail
|
||||
|
||||
import im.vector.app.core.utils.TemporaryStore
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
// Store to keep a pending action from sub screen of a room detail
|
||||
@Singleton
|
||||
class RoomDetailPendingActionStore @Inject constructor() : TemporaryStore<RoomDetailPendingAction>(10_000)
|
|
@ -274,9 +274,15 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
||||
room.getUserReadReceipt(action.userId)
|
||||
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
||||
}
|
||||
|
||||
private fun handleSendSticker(action: RoomDetailAction.SendSticker) {
|
||||
room.sendEvent(EventType.STICKER, action.stickerContent.toContent())
|
||||
}
|
||||
|
|
|
@ -172,13 +172,16 @@ class RoomMemberProfileController @Inject constructor(
|
|||
val membership = state.asyncMembership() ?: return
|
||||
|
||||
buildProfileSection(stringProvider.getString(R.string.room_profile_section_more))
|
||||
buildProfileAction(
|
||||
id = "read_receipt",
|
||||
editable = false,
|
||||
title = stringProvider.getString(R.string.room_member_jump_to_read_receipt),
|
||||
dividerColor = dividerColor,
|
||||
action = { callback?.onJumpToReadReceiptClicked() }
|
||||
)
|
||||
|
||||
if (state.hasReadReceipt) {
|
||||
buildProfileAction(
|
||||
id = "read_receipt",
|
||||
editable = false,
|
||||
title = stringProvider.getString(R.string.room_member_jump_to_read_receipt),
|
||||
dividerColor = dividerColor,
|
||||
action = { callback?.onJumpToReadReceiptClicked() }
|
||||
)
|
||||
}
|
||||
|
||||
val ignoreActionTitle = state.buildIgnoreActionTitle()
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ import im.vector.app.core.platform.VectorBaseFragment
|
|||
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.RoomDetailPendingAction
|
||||
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
|
||||
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
|
||||
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
@ -61,7 +63,8 @@ data class RoomMemberProfileArgs(
|
|||
class RoomMemberProfileFragment @Inject constructor(
|
||||
val viewModelFactory: RoomMemberProfileViewModel.Factory,
|
||||
private val roomMemberProfileController: RoomMemberProfileController,
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||
) : VectorBaseFragment(), RoomMemberProfileController.Callback {
|
||||
|
||||
private val fragmentArgs: RoomMemberProfileArgs by args()
|
||||
|
@ -276,11 +279,13 @@ class RoomMemberProfileFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onJumpToReadReceiptClicked() {
|
||||
vectorBaseActivity.notImplemented("Jump to read receipts")
|
||||
roomDetailPendingActionStore.data = RoomDetailPendingAction.JumpToReadReceipt(fragmentArgs.userId)
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
|
||||
override fun onMentionClicked() {
|
||||
vectorBaseActivity.notImplemented("Mention")
|
||||
roomDetailPendingActionStore.data = RoomDetailPendingAction.MentionUser(fragmentArgs.userId)
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
|
||||
private fun handleShareRoomMemberProfile(permalink: String) {
|
||||
|
|
|
@ -85,7 +85,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
|
|||
setState {
|
||||
copy(
|
||||
isMine = session.myUserId == this.userId,
|
||||
userMatrixItem = room?.getRoomMember(initialState.userId)?.toMatrixItem()?.let { Success(it) } ?: Uninitialized
|
||||
userMatrixItem = room?.getRoomMember(initialState.userId)?.toMatrixItem()?.let { Success(it) } ?: Uninitialized,
|
||||
hasReadReceipt = room?.getUserReadReceipt(initialState.userId) != null
|
||||
)
|
||||
}
|
||||
observeIgnoredState()
|
||||
|
|
|
@ -39,6 +39,7 @@ data class RoomMemberProfileViewState(
|
|||
val allDevicesAreTrusted: Boolean = false,
|
||||
val allDevicesAreCrossSignedTrusted: Boolean = false,
|
||||
val asyncMembership: Async<Membership> = Uninitialized,
|
||||
val hasReadReceipt: Boolean = false,
|
||||
val actionPermissions: ActionPermissions = ActionPermissions()
|
||||
) : MvRxState {
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.core.extensions.addFragment
|
|||
import im.vector.app.core.extensions.addFragmentToBackstack
|
||||
import im.vector.app.core.platform.ToolbarConfigurable
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
|
||||
import im.vector.app.features.room.RequireActiveMembershipViewEvents
|
||||
import im.vector.app.features.room.RequireActiveMembershipViewModel
|
||||
import im.vector.app.features.room.RequireActiveMembershipViewState
|
||||
|
@ -61,6 +62,9 @@ class RoomProfileActivity :
|
|||
@Inject
|
||||
lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||
|
||||
override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel {
|
||||
return requireActiveMembershipViewModelFactory.create(initialState)
|
||||
}
|
||||
|
@ -97,6 +101,13 @@ class RoomProfileActivity :
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (roomDetailPendingActionStore.data != null) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) {
|
||||
if (roomLeft.leftMessage != null) {
|
||||
Toast.makeText(this, roomLeft.leftMessage, Toast.LENGTH_LONG).show()
|
||||
|
|
Loading…
Reference in a new issue