Merge pull request #6818 from element-hq/feature/bma/markAsUnreadInRoomDetails

Add mark as read / unread in room details
This commit is contained in:
Benoit Marty 2026-05-21 09:01:52 +02:00 committed by GitHub
commit dea808ce8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 351 additions and 102 deletions

View file

@ -382,6 +382,10 @@ class LoggedInFlowNode(
}
is NavTarget.Room -> {
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
override fun onDone() {
backstack.pop()
}
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) {
lifecycleScope.launch {
attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = clearBackStack)

View file

@ -82,6 +82,7 @@ class JoinedRoomLoadedFlowNode(
plugins = plugins,
), DependencyInjectionGraphOwner {
interface Callback : Plugin {
fun onDone()
fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean = false)
fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
fun navigateToGlobalNotificationSettings()
@ -142,6 +143,10 @@ class JoinedRoomLoadedFlowNode(
private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node {
val callback = object : RoomDetailsEntryPoint.Callback {
override fun onDone() {
callback.onDone()
}
override fun navigateToGlobalNotificationSettings() {
callback.navigateToGlobalNotificationSettings()
}

View file

@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.tests.testutils.lambda.lambdaError
class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback {
override fun onDone() = lambdaError()
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) = lambdaError()
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError()
override fun navigateToGlobalNotificationSettings() = lambdaError()

View file

@ -38,6 +38,7 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint {
data class Params(val initialElement: InitialTarget) : NodeInputs
interface Callback : Plugin {
fun onDone()
fun navigateToGlobalNotificationSettings()
fun navigateToDeveloperSettings()
fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean = false)

View file

@ -40,6 +40,7 @@ dependencies {
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.preferences.api)
implementation(projects.libraries.push.api)
implementation(projects.libraries.testtags)
api(projects.features.roomdetails.api)
api(projects.libraries.usersearch.api)
@ -69,6 +70,7 @@ dependencies {
testImplementation(projects.libraries.mediaviewer.test)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.usersearch.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.features.call.test)

View file

@ -14,4 +14,6 @@ sealed interface RoomDetailsEvent {
data object UnmuteNotification : RoomDetailsEvent
data class CopyToClipboard(val text: String) : RoomDetailsEvent
data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent
data object MarkAsRead : RoomDetailsEvent
data object MarkAsUnread : RoomDetailsEvent
}

View file

@ -176,6 +176,10 @@ class RoomDetailsFlowNode(
return when (navTarget) {
NavTarget.RoomDetails -> {
val roomDetailsCallback = object : RoomDetailsNode.Callback {
override fun navigateBack() {
callback.onDone()
}
override fun navigateToRoomMemberList() {
backstack.push(NavTarget.RoomMemberList)
}

View file

@ -0,0 +1,12 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl
interface RoomDetailsNavigator {
fun onDone()
}

View file

@ -42,12 +42,13 @@ import io.element.android.libraries.androidutils.R as AndroidUtilsR
class RoomDetailsNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: RoomDetailsPresenter,
presenterFactory: RoomDetailsPresenter.Factory,
private val room: BaseRoom,
private val analyticsService: AnalyticsService,
private val leaveRoomRenderer: LeaveRoomRenderer,
) : Node(buildContext, plugins = plugins) {
) : Node(buildContext, plugins = plugins), RoomDetailsNavigator {
interface Callback : Plugin {
fun navigateBack()
fun navigateToRoomMemberList()
fun navigateToInviteMembers()
fun navigateToRoomDetailsEdit()
@ -65,6 +66,7 @@ class RoomDetailsNode(
fun navigateToSelectNewOwnersWhenLeaving()
}
private val presenter = presenterFactory.create(this)
private val callback: Callback = callback()
init {
@ -144,4 +146,8 @@ class RoomDetailsNode(
}
)
}
override fun onDone() {
callback.navigateBack()
}
}

View file

@ -17,7 +17,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.features.knockrequests.api.KnockRequestPermissions
import io.element.android.features.knockrequests.api.knockRequestPermissions
@ -44,19 +46,24 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions
import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@Inject
@AssistedInject
class RoomDetailsPresenter(
@Assisted private val navigator: RoomDetailsNavigator,
private val client: MatrixClient,
private val room: JoinedRoom,
private val notificationSettingsService: NotificationSettingsService,
@ -67,7 +74,16 @@ class RoomDetailsPresenter(
private val analyticsService: AnalyticsService,
private val clipboardHelper: ClipboardHelper,
private val appPreferencesStore: AppPreferencesStore,
private val sessionPreferencesStore: SessionPreferencesStore,
private val notificationCleaner: NotificationCleaner,
) : Presenter<RoomDetailsState> {
@AssistedFactory
interface Factory {
fun create(
navigator: RoomDetailsNavigator,
): RoomDetailsPresenter
}
@Composable
override fun present(): RoomDetailsState {
val scope = rememberCoroutineScope()
@ -79,6 +95,14 @@ class RoomDetailsPresenter(
val roomTopic by remember { derivedStateOf { roomInfo.topic } }
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
val hasNewContent by remember {
derivedStateOf {
roomInfo.numUnreadMessages > 0 ||
roomInfo.numUnreadMentions > 0 ||
roomInfo.numUnreadNotifications > 0 ||
roomInfo.isMarkedUnread
}
}
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
@ -145,6 +169,8 @@ class RoomDetailsPresenter(
clipboardHelper.copyPlainText(event.text)
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
}
is RoomDetailsEvent.MarkAsRead -> scope.markAsRead()
is RoomDetailsEvent.MarkAsUnread -> scope.markAsUnread()
}
}
@ -188,6 +214,7 @@ class RoomDetailsPresenter(
showDebugInfo = isDeveloperModeEnabled,
roomVersion = roomInfo.roomVersion,
roomHistoryVisibility = roomInfo.historyVisibility,
hasNewContent = hasNewContent,
eventSink = ::handleEvent,
)
}
@ -241,4 +268,26 @@ class RoomDetailsPresenter(
analyticsService.captureInteraction(Interaction.Name.MobileRoomFavouriteToggle)
}
}
private fun CoroutineScope.markAsRead() = launch {
notificationCleaner.clearMessagesForRoom(client.sessionId, room.roomId)
room.setUnreadFlag(isUnread = false)
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
ReceiptType.READ
} else {
ReceiptType.READ_PRIVATE
}
room.markAsRead(receiptType)
.onSuccess {
analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle)
}
}
private fun CoroutineScope.markAsUnread() = launch {
room.setUnreadFlag(isUnread = true)
.onSuccess {
analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle)
navigator.onDone()
}
}
}

View file

@ -52,6 +52,7 @@ data class RoomDetailsState(
val showDebugInfo: Boolean,
val roomVersion: String?,
val roomHistoryVisibility: RoomHistoryVisibility,
val hasNewContent: Boolean,
val eventSink: (RoomDetailsEvent) -> Unit
) {
val roomBadges = buildList {

View file

@ -34,7 +34,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState>
override val values: Sequence<RoomDetailsState>
get() = sequenceOf(
aRoomDetailsState(displayAdminSettings = true),
aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true),
aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true, hasNewContent = true),
aRoomDetailsState(roomTopic = RoomTopicState.CanAddTopic),
aRoomDetailsState(isEncrypted = false),
aRoomDetailsState(roomAlias = null),
@ -123,6 +123,7 @@ fun aRoomDetailsState(
isTombstoned: Boolean = false,
showDebugInfo: Boolean = false,
roomHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Shared,
hasNewContent: Boolean = false,
eventSink: (RoomDetailsEvent) -> Unit = {},
) = RoomDetailsState(
roomId = roomId,
@ -154,6 +155,7 @@ fun aRoomDetailsState(
showDebugInfo = showDebugInfo,
roomVersion = "12",
roomHistoryVisibility = roomHistoryVisibility,
hasNewContent = hasNewContent,
eventSink = eventSink,
)

View file

@ -8,6 +8,7 @@
package io.element.android.features.roomdetails.impl
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@ -188,6 +189,46 @@ fun RoomDetailsView(
)
}
PreferenceCategory {
if (state.hasNewContent) {
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.screen_roomlist_mark_as_read),
style = MaterialTheme.typography.bodyLarge,
)
},
onClick = {
state.eventSink(RoomDetailsEvent.MarkAsRead)
},
leadingContent = ListItemContent.Icon(
iconSource = IconSource.Vector(CompoundIcons.MarkAsRead())
),
trailingContent = ListItemContent.Custom {
Box(
modifier = modifier
.size(8.dp)
.clip(CircleShape)
.background(ElementTheme.colors.iconAccentPrimary)
)
},
)
} else {
ListItem(
headlineContent = {
Text(
text = stringResource(id = R.string.screen_roomlist_mark_as_unread),
)
},
onClick = {
state.eventSink(RoomDetailsEvent.MarkAsUnread)
},
leadingContent = ListItemContent.Icon(
iconSource = IconSource.Vector(CompoundIcons.MarkAsUnread())
),
)
}
}
PreferenceCategory {
if (state.roomNotificationSettings != null) {
NotificationItem(

View file

@ -132,6 +132,8 @@
<string name="screen_room_roles_and_permissions_roles_header">"Roles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Room details"</string>
<string name="screen_room_roles_and_permissions_title">"Roles &amp; permissions"</string>
<string name="screen_roomlist_mark_as_read">"Mark as read"</string>
<string name="screen_roomlist_mark_as_unread">"Mark as unread"</string>
<string name="screen_security_and_privacy_add_room_address_action">"Add address"</string>
<string name="screen_security_and_privacy_ask_to_join_multiple_spaces_members_option_description">"Anyone in authorised spaces can join, but everyone else must request access."</string>
<string name="screen_security_and_privacy_ask_to_join_option_description">"Everyone must request access."</string>

View file

@ -68,6 +68,7 @@ class DefaultRoomDetailsEntryPointTest {
)
}
val callback = object : RoomDetailsEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun navigateToGlobalNotificationSettings() = lambdaError()
override fun navigateToDeveloperSettings() = lambdaError()
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) = lambdaError()

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.roomdetails.impl
import io.element.android.tests.testutils.lambda.lambdaError
class FakeRoomDetailsNavigator(
private val onDoneResult: () -> Unit = { lambdaError() }
) : RoomDetailsNavigator {
override fun onDone() = onDoneResult()
}

View file

@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
import io.element.android.libraries.matrix.test.A_ROOM_ID
@ -28,7 +29,7 @@ import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions
import io.element.android.tests.testutils.lambda.lambdaError
fun aRoom(
fun aFakeBaseRoom(
sessionId: SessionId = A_SESSION_ID,
roomId: RoomId = A_ROOM_ID,
displayName: String = A_ROOM_NAME,
@ -49,6 +50,7 @@ fun aRoom(
getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
userRoleResult: () -> Result<RoomMember.Role> = { lambdaError() },
setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() },
markAsReadResult: (ReceiptType) -> Result<Unit> = { lambdaError() },
) = FakeBaseRoom(
sessionId = sessionId,
roomId = roomId,
@ -57,6 +59,7 @@ fun aRoom(
getUpdatedMemberResult = getUpdatedMemberResult,
userRoleResult = userRoleResult,
setIsFavoriteResult = setIsFavoriteResult,
markAsReadResult = markAsReadResult,
roomPermissions = roomPermissions,
initialRoomInfo = aRoomInfo(
name = displayName,
@ -106,6 +109,7 @@ fun aJoinedRoom(
publishRoomAliasInRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
removeRoomAliasFromRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() },
markAsReadResult: (ReceiptType) -> Result<Unit> = { lambdaError() },
) = FakeJoinedRoom(
roomNotificationSettingsService = notificationSettingsService,
setNameResult = setNameResult,
@ -118,7 +122,7 @@ fun aJoinedRoom(
updateCanonicalAliasResult = updateCanonicalAliasResult,
publishRoomAliasInRoomDirectoryResult = publishRoomAliasInRoomDirectoryResult,
removeRoomAliasFromRoomDirectoryResult = removeRoomAliasFromRoomDirectoryResult,
baseRoom = aRoom(
baseRoom = aFakeBaseRoom(
sessionId = sessionId,
roomId = roomId,
roomPermissions = roomPermissions,
@ -139,5 +143,6 @@ fun aJoinedRoom(
joinedMemberCount = joinedMemberCount,
activeMemberCount = activeMemberCount,
invitedMemberCount = invitedMemberCount,
markAsReadResult = markAsReadResult,
)
)

View file

@ -21,6 +21,8 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.RoomMembersState
@ -28,6 +30,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_NAME
@ -41,7 +44,11 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.EventsRecorder
@ -79,7 +86,10 @@ class RoomDetailsPresenterTest {
analyticsService: AnalyticsService = FakeAnalyticsService(),
encryptionService: FakeEncryptionService = FakeEncryptionService(),
clipboardHelper: ClipboardHelper = FakeClipboardHelper(),
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore()
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
navigator: RoomDetailsNavigator = FakeRoomDetailsNavigator(),
notificationCleaner: NotificationCleaner = FakeNotificationCleaner(),
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
): RoomDetailsPresenter {
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory {
@ -96,6 +106,7 @@ class RoomDetailsPresenterTest {
}
}
return RoomDetailsPresenter(
navigator = navigator,
client = matrixClient,
room = room,
notificationSettingsService = matrixClient.notificationSettingsService,
@ -106,6 +117,8 @@ class RoomDetailsPresenterTest {
analyticsService = analyticsService,
clipboardHelper = clipboardHelper,
appPreferencesStore = appPreferencesStore,
notificationCleaner = notificationCleaner,
sessionPreferencesStore = sessionPreferencesStore,
)
}
@ -598,6 +611,87 @@ class RoomDetailsPresenterTest {
}
}
@Test
fun `present - mark as read`() = runTest {
val markAsReadResult = lambdaRecorder<ReceiptType, Result<Unit>> { _ -> Result.success(Unit) }
val room = aJoinedRoom(
markAsReadResult = markAsReadResult,
)
val clearMessagesForRoomResult = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> Result.success(Unit) }
val notificationCleaner = FakeNotificationCleaner(
clearMessagesForRoomLambda = clearMessagesForRoomResult,
)
val presenter = createRoomDetailsPresenter(
room = room,
notificationCleaner = notificationCleaner,
)
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
skipItems(1)
with(awaitItem()) {
eventSink(RoomDetailsEvent.MarkAsRead)
}
assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false)
markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ))
clearMessagesForRoomResult.assertions().isCalledOnce().with(
value(room.sessionId),
value(room.roomId),
)
}
}
@Test
fun `present - mark as read - private`() = runTest {
val markAsReadResult = lambdaRecorder<ReceiptType, Result<Unit>> { _ -> Result.success(Unit) }
val room = aJoinedRoom(
markAsReadResult = markAsReadResult,
)
val sessionPreferencesStore = InMemorySessionPreferencesStore(
isSendPublicReadReceiptsEnabled = false,
)
val clearMessagesForRoomResult = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> Result.success(Unit) }
val notificationCleaner = FakeNotificationCleaner(
clearMessagesForRoomLambda = clearMessagesForRoomResult,
)
val presenter = createRoomDetailsPresenter(
room = room,
notificationCleaner = notificationCleaner,
sessionPreferencesStore = sessionPreferencesStore,
)
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
skipItems(1)
with(awaitItem()) {
eventSink(RoomDetailsEvent.MarkAsRead)
}
assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false)
markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ_PRIVATE))
clearMessagesForRoomResult.assertions().isCalledOnce().with(
value(room.sessionId),
value(room.roomId),
)
}
}
@Test
fun `present - mark as unread`() = runTest {
val room = aJoinedRoom()
val onDoneResult = lambdaRecorder<Unit> { }
val navigator = FakeRoomDetailsNavigator(
onDoneResult = onDoneResult
)
val presenter = createRoomDetailsPresenter(
room = room,
navigator = navigator,
)
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
skipItems(1)
with(awaitItem()) {
eventSink(RoomDetailsEvent.MarkAsUnread)
}
onDoneResult.assertions().isCalledOnce()
assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(true)
}
}
private fun roomPermissions(
canInvite: Boolean = true,
canKick: Boolean = true,

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34ce2f0451fb1681648c91771316256e67b1f295f4deb97f6115ae593d82f2ef
size 83472
oid sha256:d7c187e173ae6d02d6c95c03bcc24cce91b0922e6aa5a08677732748aab0fc30
size 85320

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eeb2355a693a7ac522028833fea4f0506e1b455e4aee888ed314c2700a9a87af
size 45454
oid sha256:8979ec6209ad6e0deb6946e00ce227f3db41a565218afe31b72b889bee224daa
size 46268

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b45d35b68d232fc5cbaf3b01156127a77618bcf2197375ad42f61bc7ad8ec400
size 44161
oid sha256:16200632461dcbbf0f6211b7064b61a1e2fa04cd34e14946beb12b9e9f4d7b7f
size 43901

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a7efaa997737bfc6db898987b78077ec3c8039a6d0cce84d770bcabcbdc815e
size 43161
oid sha256:2279893a284e620cf487f6370982a14278fd57bb278e53932a5fd7c5cab8e95e
size 43033

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a8de9a9297383ff1ec552304a49040c44236654dd16ba9ae5f54fddf3e14c617
size 44764
oid sha256:5d4da60a94c56b13b65efb797825c54c1e107fe4440f56b1fcac5d47779cbf64
size 44502

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff4c856028887c1390df7b313462e37e0723ff98b9f92aa769b2fc9133ab6ec5
size 44673
oid sha256:ee2899f7870b09f287b45b327d5d0462b423ba65afa1087dec1be862507378de
size 44411

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:162a9cd8b10b8ec7c33dc926c63d2d04986ca0e29a6cd036771ffc137176e28c
size 45079
oid sha256:78ba2ddfdfc47febc0503daa0985a5fdf04ef15bdc391b4a88c3c630b1946b35
size 46004

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fcd3e86c18e34e96a2b535340a27a8cb248d48697ce75d1c7c62cde27b3e2ccb
size 45577
oid sha256:4a832b0e395498b15e176529ea55c50ed129f6d2330fbec4ff1f8e3dcaeab93f
size 46543

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5a20887fa9a6dd7c96f328f24608e1fcea0326b60cf8a7b43e2387fa19595f5
size 44932
oid sha256:d8e738bd873339d195b4190f65a18016f2f0eb1bbc9f513a2b25467cb5f5828c
size 44745

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8087a634dd7bf3893697d8ab599f9694169e878b4484b0d8e9821307a8423d63
size 44243
oid sha256:892dd301ab08c6a302e8286d3f86878178130a22baf550402f2102ba98f0d21c
size 43982

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2f239cb428d2e4cffa86e8d8397904af0c0c5e621585f31bcf3135cdd2c81a40
size 40541
oid sha256:809c597757d6f1385803c1378a494f24309b533168df59c7e06852c1ce34b8d3
size 41408

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3bcc557108fc16a1f63d22b3512d271d30dccc801838607e78921baf9ba490c0
size 40509
oid sha256:3756793e5d92f0fac18e96a49e69ac7ed6901c1195d3e6526088027405e82dbf
size 41361

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7b4d5f81f605a0858994e50d72fbd17d2f82e0b2e4673ff9baf6e25a103ef55
size 40392
oid sha256:099f571485ce8476d4b8a2313610b6c1d03a7d7420132e06d6258b483d640a08
size 39130

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fef854b4c908e206ece79491c655fbf0fd5b51883f3d77045c579664331b9058
size 45195
oid sha256:66fd2cfb9f343c77b8db0a4fbc0d7a1571a4dfe1d70c78823a4ba03bfbc846eb
size 44934

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dde778a13f36958fefba950fe0d3470b7277d22fe4eac189c77b0838b3728ef3
size 44940
oid sha256:465ecd337f705262c8f533b3b01daa6a548c6ee88752be405609660ddfaf56b1
size 44676

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5870cd12f6de78ecf72a9dc8e3716e0e3476ecf2f29647fc1eca900f5e030f9
size 44652
oid sha256:3cca233c171b6c6709a94163ca1f0024a42ff66200383fdbcaf57b321251522e
size 44390

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1281394671737e78232cb7d9785b57b8ef2ca50c125f0c0854d101d27efaa939
size 37910
oid sha256:9e5e8b39197d115c6190b89fd54033b3319b36dfbf5c6f152dd84e6c79147bbb
size 37877

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ec2b5711ea9ffacc6aad4f7b16efdcc78603e9eaa9d33abfaa0d867db5aead67
size 42355
oid sha256:4899d2ffcc6da1f414f7fb223440b8463250dc5ac07558cfaee2947f95c93362
size 42236

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4adfa037ff619098067d03f252ba0af1c3c89553ea0c81dcda41f7f0b655baad
size 42450
oid sha256:577381a33e7f0a88ceebb7f2be6528c3e99417cf1351b74c134aab948664cc64
size 42149

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:76f63f6c98318762a574d7fb04c8e23ad8312b1fc9adc404669ac6d9e2c42097
size 40127
oid sha256:b2bc2699d8dcf770103726c571a6966eacf173cbbad05c4fa6188815aa9ae231
size 41052

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3063251e98ea6e797e5178cb954d99e71fc1422df19c24a59b1395531380e941
size 41113
oid sha256:9cba49398954a404def6b37f3cde057cb88d6f59249b943f1689403ccd902326
size 42024

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0d5b8a9f7bc2e7f654b018b2f66b5971c21326dd6b64371f330299649daea4c0
size 45755
oid sha256:405a29c158295616560152c5790f189a8019bd635a53a69b581c8aa595e6e9ea
size 45494

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2dd6b524bd146b4e66621fea59515e6784055f52f6d1d08c393b5be208e5f29a
size 44657
oid sha256:3f948e9cc2eb7bc73b0441fbdea72d582a042895b3404c8302b7e182c3481fae
size 44425

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:73a27b8df68182a3d0ae4872a14688ae10e5651ec85490c1e46186acc2c60028
size 44671
oid sha256:b4702b18ad5d203050140e6e1a18553344f52eb5482fcaf6ab497698b9ec59fd
size 44432

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bedc6d0b55d94586de728cb4039303c9ecbd8b3a090c401331ae1bf6e0ba426
size 46396
oid sha256:4189b58dd1263a81566ea86d9881c02f053ecb2a91ba9442553fbe0f230e6860
size 47105

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d47a7207e83fe770d4925b91b4bb0eee6cb2c494c46b1f3d5d6d0720464392d6
size 44992
oid sha256:3fced9019f81e5804ea57c0b1a2750868626ad35b9a7227009a796d289595556
size 44659

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:605ea373238bbaa4d3820adf13bf3896d9a5c5b6ceb6c022a7f3b8ae817f921f
size 43927
oid sha256:be9bb839a503d169d67d4332a133ce2b3d76b3ff4c52c063436521f8a74c973e
size 43894

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4dab72328925eb45cd529913972670587ea4257d2ff7affd708295d68e783d7
size 45579
oid sha256:82c2781c37b7cefa198f2061a2c8e900b27bd57653db88733be5ff55dfaf5eee
size 45251

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:76cc029a07c0fb568d9e15864be667478cd9927095f66ec2f590a1466c58a291
size 45497
oid sha256:36feab599a6a2d4269b68f42149a6f82832ac0f6f0e9f01fb49adc21b3a58d93
size 45172

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:42a8b9b5abe3a5a3fe61e27924ca75ea7925e857005d5de3cd3ceb81500f73c2
size 45981
oid sha256:04bedd62b5ceba766197dd18da9efb0ff086c2c01c9c19446f8a3c42ed43ad87
size 46813

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19cc2df1da3b685668afe309bd305d5ac8447d0997c7ad902685d44daa26239f
size 46556
oid sha256:828b065aaeeccd826e98e9601069819d7a75ffabab9820a834cacb627416edc0
size 47409

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5625bb01c24d969adeecdb536e145ba549ef1c10d99e16f7a68c3bd577fbefbc
size 45804
oid sha256:4740bcd118f8583334dd1c03b5011e33c5384ea6810d1c83711f538cf9f1d165
size 45521

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3bdd0b00fddd9fc62b5c9c97b5ff766591c0a1822730bd249059ed02508a65ee
size 45353
oid sha256:7fab53a63021ddae8384aed8eaf0a3803d06e8c80645ecc13f79a7f5cced6e68
size 45024

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6fb58a8db96c0e76d5a0e8c8d2a38ae206e3dade479bd924fd404902c8e69b42
size 41322
oid sha256:89a2c619412ad9ca43c79cb76ebe72b827da71743dda7044a9b44de2990f160b
size 42388

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5030731135dec08034e1992499caa251aae8039cebafb09210fc83d622b06bd9
size 41257
oid sha256:56ceed440bb51039ffe7e4419477a57a235eed153a0188838e6265c241896a82
size 42262

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4dd437c614c904bf210e5b51fe017f26cf849e7572b09966e834ac6fd429d347
size 41380
oid sha256:3b03e9da2d73cc0e3d0485b73470e576d7ebd4b8b5f375f2a478f1c1524772fa
size 39948

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:380fbdd2caa485b9fe84a331e64139ccb23998ce1d95158c6345364daa3810b8
size 46048
oid sha256:a686a2596b9b85b17c6882aa986b12e13a83a47ed217c4eb6de6aa2cb3ec2d50
size 45717

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4610f8488c8c8edf05e2cd3e09470660b2c4db14650907c28367d2b38bce852
size 45792
oid sha256:d6bda369c1754e2c07216f4bff0285e017f87b550fe0dc6bb5f127845d52c9b6
size 45462

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:efb795d2808d7ae5e38eda42105101dce377154c794844f752e4cb80dcabd681
size 45462
oid sha256:285045769d912e98df0371c829affd925642c8a20cd0b6b09dfa92ab5f143538
size 45131

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7dca0800e7e68e65bd27814b4c86123da40385d6e85153e003ba58ca7689b5f8
size 38742
oid sha256:463548e170b48509d68c0993acb000748b19921e5d4be784b07ce83252c8a5bb
size 38613

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:351183a1b0fb44dc42c790a625ee6aa5740b27fa95b138252b72bf67268f5a16
size 43015
oid sha256:c1d19c2b8c6b3c80b68214da2bb9f54a6897a279aa70e2d8cab88c291332cece
size 42980

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:31f92cc4d97c7129bc37da51e6f0a10612d4f61803762b1709c5d72f1cda86f1
size 43337
oid sha256:2057479cad5d3b9a89ec835ee1d06705c6c459f7810c2875bea26b975944bcf7
size 42960

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e54b946de1128b1109ece7553e73d7676199edc45e7f275014c37503336d302e
size 40862
oid sha256:073addf26e8d0b638bce93db99d1fe9740abd89613a1eb8ef5bb4324e489d6fa
size 41924

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4f3df6d78498d2b390cf926fb1111fdfa0f38ba5dd9e023b9bd2af42c3951511
size 41921
oid sha256:56d72cab4a9d7af568cc497b68d6ca9055874544cfd462c93da37fe368801fdf
size 42984

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4e15030fc182998a37493228d91c888d6b499b6a684b1a4e15d402c9156a40ba
size 46678
oid sha256:9141f5fa4e054dc5cf70126309830bc38d564d467a215eaa1130f3601b15e761
size 46350

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b773cce165a6410140afbf0daf3ec526233b97ea707811a3794e591704237398
size 45536
oid sha256:a062b8cbe4dfbd94a5b068b1f7c4654ed8b697a7e98ae4c2bcb6c3e5bf473b49
size 45289

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f28bec4fb21e827f3467bc08ab0f33e917cf76360903b6e7df6282a7589cae7e
size 45545
oid sha256:46aafd39e1a93f8d357def4c5f3f024ad0ee13931ebdb9f72475dbc911fd7532
size 45266

View file

@ -221,6 +221,7 @@
"screen_notification_settings_mentions_only_disclaimer",
"screen_room_change_.*",
"screen_room_roles_.*",
"screen_roomlist_mark_as_.*",
"screen\\.edit_room_address\\..*",
"screen\\.security_and_privacy\\..*"
]