mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
This commit is contained in:
parent
1fd24e746c
commit
7952e205b9
@ -15,6 +15,7 @@ Improvements 🙌:
|
|||||||
- Add a menu item in the timeline as a shortcut to invite user (#2171)
|
- Add a menu item in the timeline as a shortcut to invite user (#2171)
|
||||||
- Drawer: move settings access and add sign out action (#2171)
|
- Drawer: move settings access and add sign out action (#2171)
|
||||||
- Filter room member (and banned users) by name (#2184)
|
- Filter room member (and banned users) by name (#2184)
|
||||||
|
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
||||||
|
@ -63,6 +63,14 @@ interface ReadService {
|
|||||||
*/
|
*/
|
||||||
fun getMyReadReceiptLive(): LiveData<Optional<String>>
|
fun getMyReadReceiptLive(): LiveData<Optional<String>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the eventId where the read receipt for the provided user is
|
||||||
|
* @param otherUserId the userId param to look for
|
||||||
|
*
|
||||||
|
* @return the eventId where the read receipt for the provided user is attached, or null if not found
|
||||||
|
*/
|
||||||
|
fun getUserReadReceipt(otherUserId: String): String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a live list of read receipts for a given event
|
* Returns a live list of read receipts for a given event
|
||||||
* @param eventId: the event
|
* @param eventId: the event
|
||||||
|
@ -107,6 +107,16 @@ internal class DefaultReadService @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUserReadReceipt(otherUserId: String): String? {
|
||||||
|
var eventId: String? = null
|
||||||
|
monarchy.doWithRealm {
|
||||||
|
eventId = ReadReceiptEntity.where(it, roomId = roomId, userId = otherUserId)
|
||||||
|
.findFirst()
|
||||||
|
?.eventId
|
||||||
|
}
|
||||||
|
return eventId
|
||||||
|
}
|
||||||
|
|
||||||
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
||||||
val liveRealmData = monarchy.findAllMappedWithChanges(
|
val liveRealmData = monarchy.findAllMappedWithChanges(
|
||||||
{ ReadReceiptsSummaryEntity.where(it, eventId) },
|
{ 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.grouplist.SelectedGroupDataSource
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.HomeRoomListDataSource
|
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.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||||
import im.vector.app.features.html.EventHtmlRenderer
|
import im.vector.app.features.html.EventHtmlRenderer
|
||||||
import im.vector.app.features.html.VectorHtmlCompressor
|
import im.vector.app.features.html.VectorHtmlCompressor
|
||||||
@ -114,6 +115,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun selectedGroupStore(): SelectedGroupDataSource
|
fun selectedGroupStore(): SelectedGroupDataSource
|
||||||
|
|
||||||
|
fun roomDetailPendingActionStore(): RoomDetailPendingActionStore
|
||||||
|
|
||||||
fun activeSessionObservableStore(): ActiveSessionDataSource
|
fun activeSessionObservableStore(): ActiveSessionDataSource
|
||||||
|
|
||||||
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
|
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
|
||||||
|
@ -23,6 +23,7 @@ const val THREE_MINUTES = 3 * 60_000L
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Store an object T for a specific period of time
|
* 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) {
|
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
|
var data: T? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
|
||||||
timer?.cancel()
|
timer?.cancel()
|
||||||
timer = Timer().also {
|
field = value
|
||||||
it.schedule(object : TimerTask() {
|
if (value != null) {
|
||||||
override fun run() {
|
timer = Timer().also {
|
||||||
field = null
|
it.schedule(object : TimerTask() {
|
||||||
}
|
override fun run() {
|
||||||
}, delay)
|
field = null
|
||||||
|
}
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,4 +88,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||||||
data class EnsureNativeWidgetAllowed(val widget: Widget,
|
data class EnsureNativeWidgetAllowed(val widget: Widget,
|
||||||
val userJustAccepted: Boolean,
|
val userJustAccepted: Boolean,
|
||||||
val grantedEvents: RoomDetailViewEvents) : RoomDetailAction()
|
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 notificationUtils: NotificationUtils,
|
||||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||||
private val imageContentRenderer: ImageContentRenderer
|
private val imageContentRenderer: ImageContentRenderer,
|
||||||
|
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||||
) :
|
) :
|
||||||
VectorBaseFragment(),
|
VectorBaseFragment(),
|
||||||
TimelineEventController.Callback,
|
TimelineEventController.Callback,
|
||||||
@ -878,6 +879,17 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId)
|
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() {
|
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.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||||
|
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
||||||
|
room.getUserReadReceipt(action.userId)
|
||||||
|
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleSendSticker(action: RoomDetailAction.SendSticker) {
|
private fun handleSendSticker(action: RoomDetailAction.SendSticker) {
|
||||||
room.sendEvent(EventType.STICKER, action.stickerContent.toContent())
|
room.sendEvent(EventType.STICKER, action.stickerContent.toContent())
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ import im.vector.app.core.platform.VectorBaseFragment
|
|||||||
import im.vector.app.core.utils.startSharePlainTextIntent
|
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
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.devices.DeviceListBottomSheet
|
||||||
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
|
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@ -61,7 +63,8 @@ data class RoomMemberProfileArgs(
|
|||||||
class RoomMemberProfileFragment @Inject constructor(
|
class RoomMemberProfileFragment @Inject constructor(
|
||||||
val viewModelFactory: RoomMemberProfileViewModel.Factory,
|
val viewModelFactory: RoomMemberProfileViewModel.Factory,
|
||||||
private val roomMemberProfileController: RoomMemberProfileController,
|
private val roomMemberProfileController: RoomMemberProfileController,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||||
) : VectorBaseFragment(), RoomMemberProfileController.Callback {
|
) : VectorBaseFragment(), RoomMemberProfileController.Callback {
|
||||||
|
|
||||||
private val fragmentArgs: RoomMemberProfileArgs by args()
|
private val fragmentArgs: RoomMemberProfileArgs by args()
|
||||||
@ -276,11 +279,13 @@ class RoomMemberProfileFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onJumpToReadReceiptClicked() {
|
override fun onJumpToReadReceiptClicked() {
|
||||||
vectorBaseActivity.notImplemented("Jump to read receipts")
|
roomDetailPendingActionStore.data = RoomDetailPendingAction.JumpToReadReceipt(fragmentArgs.userId)
|
||||||
|
vectorBaseActivity.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMentionClicked() {
|
override fun onMentionClicked() {
|
||||||
vectorBaseActivity.notImplemented("Mention")
|
roomDetailPendingActionStore.data = RoomDetailPendingAction.MentionUser(fragmentArgs.userId)
|
||||||
|
vectorBaseActivity.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleShareRoomMemberProfile(permalink: String) {
|
private fun handleShareRoomMemberProfile(permalink: String) {
|
||||||
|
@ -36,6 +36,7 @@ import im.vector.app.core.extensions.cleanup
|
|||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
|
||||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
|
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -43,7 +44,8 @@ import javax.inject.Inject
|
|||||||
class RoomMemberListFragment @Inject constructor(
|
class RoomMemberListFragment @Inject constructor(
|
||||||
val viewModelFactory: RoomMemberListViewModel.Factory,
|
val viewModelFactory: RoomMemberListViewModel.Factory,
|
||||||
private val roomMemberListController: RoomMemberListController,
|
private val roomMemberListController: RoomMemberListController,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||||
) : VectorBaseFragment(), RoomMemberListController.Callback {
|
) : VectorBaseFragment(), RoomMemberListController.Callback {
|
||||||
|
|
||||||
private val viewModel: RoomMemberListViewModel by fragmentViewModel()
|
private val viewModel: RoomMemberListViewModel by fragmentViewModel()
|
||||||
@ -96,6 +98,13 @@ class RoomMemberListFragment @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (roomDetailPendingActionStore.data != null) {
|
||||||
|
vectorBaseActivity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
recyclerView.cleanup()
|
recyclerView.cleanup()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
Loading…
Reference in New Issue
Block a user