parent
7571289347
commit
1261d0ddee
8 changed files with 89 additions and 1 deletions
|
|
@ -40,6 +40,7 @@ dependencies {
|
||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.permissions.api)
|
implementation(projects.libraries.permissions.api)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
|
implementation(projects.libraries.push.api)
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
api(projects.features.roomdetails.api)
|
api(projects.features.roomdetails.api)
|
||||||
api(projects.libraries.usersearch.api)
|
api(projects.libraries.usersearch.api)
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,6 @@ sealed interface RoomDetailsEvent {
|
||||||
data object UnmuteNotification : RoomDetailsEvent
|
data object UnmuteNotification : RoomDetailsEvent
|
||||||
data class CopyToClipboard(val text: String) : RoomDetailsEvent
|
data class CopyToClipboard(val text: String) : RoomDetailsEvent
|
||||||
data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent
|
data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent
|
||||||
|
data object MarkAsRead : RoomDetailsEvent
|
||||||
|
data object MarkAsUnread : RoomDetailsEvent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,17 @@ 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.canEditRolesAndPermissions
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState
|
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.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.getDirectRoomMember
|
||||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
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.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.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -67,6 +71,8 @@ class RoomDetailsPresenter(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
private val clipboardHelper: ClipboardHelper,
|
private val clipboardHelper: ClipboardHelper,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
|
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||||
|
private val notificationCleaner: NotificationCleaner,
|
||||||
) : Presenter<RoomDetailsState> {
|
) : Presenter<RoomDetailsState> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomDetailsState {
|
override fun present(): RoomDetailsState {
|
||||||
|
|
@ -79,6 +85,14 @@ class RoomDetailsPresenter(
|
||||||
val roomTopic by remember { derivedStateOf { roomInfo.topic } }
|
val roomTopic by remember { derivedStateOf { roomInfo.topic } }
|
||||||
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
||||||
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
|
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 } }
|
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
|
||||||
|
|
||||||
|
|
@ -145,6 +159,8 @@ class RoomDetailsPresenter(
|
||||||
clipboardHelper.copyPlainText(event.text)
|
clipboardHelper.copyPlainText(event.text)
|
||||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
|
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
|
||||||
}
|
}
|
||||||
|
is RoomDetailsEvent.MarkAsRead -> scope.markAsRead()
|
||||||
|
is RoomDetailsEvent.MarkAsUnread -> scope.markAsUnread()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,6 +204,7 @@ class RoomDetailsPresenter(
|
||||||
showDebugInfo = isDeveloperModeEnabled,
|
showDebugInfo = isDeveloperModeEnabled,
|
||||||
roomVersion = roomInfo.roomVersion,
|
roomVersion = roomInfo.roomVersion,
|
||||||
roomHistoryVisibility = roomInfo.historyVisibility,
|
roomHistoryVisibility = roomInfo.historyVisibility,
|
||||||
|
hasNewContent = hasNewContent,
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -241,4 +258,25 @@ class RoomDetailsPresenter(
|
||||||
analyticsService.captureInteraction(Interaction.Name.MobileRoomFavouriteToggle)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ data class RoomDetailsState(
|
||||||
val showDebugInfo: Boolean,
|
val showDebugInfo: Boolean,
|
||||||
val roomVersion: String?,
|
val roomVersion: String?,
|
||||||
val roomHistoryVisibility: RoomHistoryVisibility,
|
val roomHistoryVisibility: RoomHistoryVisibility,
|
||||||
|
val hasNewContent: Boolean,
|
||||||
val eventSink: (RoomDetailsEvent) -> Unit
|
val eventSink: (RoomDetailsEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
val roomBadges = buildList {
|
val roomBadges = buildList {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState>
|
||||||
override val values: Sequence<RoomDetailsState>
|
override val values: Sequence<RoomDetailsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aRoomDetailsState(displayAdminSettings = true),
|
aRoomDetailsState(displayAdminSettings = true),
|
||||||
aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true),
|
aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true, hasNewContent = true),
|
||||||
aRoomDetailsState(roomTopic = RoomTopicState.CanAddTopic),
|
aRoomDetailsState(roomTopic = RoomTopicState.CanAddTopic),
|
||||||
aRoomDetailsState(isEncrypted = false),
|
aRoomDetailsState(isEncrypted = false),
|
||||||
aRoomDetailsState(roomAlias = null),
|
aRoomDetailsState(roomAlias = null),
|
||||||
|
|
@ -123,6 +123,7 @@ fun aRoomDetailsState(
|
||||||
isTombstoned: Boolean = false,
|
isTombstoned: Boolean = false,
|
||||||
showDebugInfo: Boolean = false,
|
showDebugInfo: Boolean = false,
|
||||||
roomHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Shared,
|
roomHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Shared,
|
||||||
|
hasNewContent: Boolean = false,
|
||||||
eventSink: (RoomDetailsEvent) -> Unit = {},
|
eventSink: (RoomDetailsEvent) -> Unit = {},
|
||||||
) = RoomDetailsState(
|
) = RoomDetailsState(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
|
@ -154,6 +155,7 @@ fun aRoomDetailsState(
|
||||||
showDebugInfo = showDebugInfo,
|
showDebugInfo = showDebugInfo,
|
||||||
roomVersion = "12",
|
roomVersion = "12",
|
||||||
roomHistoryVisibility = roomHistoryVisibility,
|
roomHistoryVisibility = roomHistoryVisibility,
|
||||||
|
hasNewContent = hasNewContent,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl
|
package io.element.android.features.roomdetails.impl
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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 {
|
PreferenceCategory {
|
||||||
if (state.roomNotificationSettings != null) {
|
if (state.roomNotificationSettings != null) {
|
||||||
NotificationItem(
|
NotificationItem(
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,8 @@
|
||||||
<string name="screen_room_roles_and_permissions_roles_header">"Roles"</string>
|
<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_room_details">"Room details"</string>
|
||||||
<string name="screen_room_roles_and_permissions_title">"Roles & permissions"</string>
|
<string name="screen_room_roles_and_permissions_title">"Roles & 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_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_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>
|
<string name="screen_security_and_privacy_ask_to_join_option_description">"Everyone must request access."</string>
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,7 @@
|
||||||
"screen_notification_settings_mentions_only_disclaimer",
|
"screen_notification_settings_mentions_only_disclaimer",
|
||||||
"screen_room_change_.*",
|
"screen_room_change_.*",
|
||||||
"screen_room_roles_.*",
|
"screen_room_roles_.*",
|
||||||
|
"screen_roomlist_mark_as_.*",
|
||||||
"screen\\.edit_room_address\\..*",
|
"screen\\.edit_room_address\\..*",
|
||||||
"screen\\.security_and_privacy\\..*"
|
"screen\\.security_and_privacy\\..*"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue