favorite : branch RoomNotableTags methods
This commit is contained in:
@@ -20,4 +20,5 @@ sealed interface RoomDetailsEvent {
|
||||
data object LeaveRoom : RoomDetailsEvent
|
||||
data object MuteNotification : RoomDetailsEvent
|
||||
data object UnmuteNotification : RoomDetailsEvent
|
||||
data class SetIsFavorite(val isFavorite: Boolean) : RoomDetailsEvent
|
||||
}
|
||||
|
||||
@@ -44,9 +44,11 @@ import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
@@ -95,6 +97,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
val dmMember by room.getDirectRoomMember(membersState)
|
||||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||
val roomType by getRoomType(dmMember)
|
||||
val isFavorite by isFavorite()
|
||||
|
||||
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
||||
val topic = roomTopic
|
||||
@@ -122,6 +125,12 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne)
|
||||
}
|
||||
}
|
||||
is RoomDetailsEvent.SetIsFavorite -> {
|
||||
scope.launch(dispatchers.io) {
|
||||
val tags = RoomNotableTags(isFavorite = event.isFavorite)
|
||||
room.updateNotableTags(tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +151,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
roomMemberDetailsState = roomMemberDetailsState,
|
||||
leaveRoomState = leaveRoomState,
|
||||
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||
isFavorite = isFavorite,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
@@ -164,6 +174,11 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun isFavorite() = remember {
|
||||
room.notableTagsFlow.map { it.isFavorite }
|
||||
}.collectAsState(initial = false)
|
||||
|
||||
@Composable
|
||||
private fun getCanInvite(membersState: MatrixRoomMembersState) = produceState(false, membersState) {
|
||||
value = room.canInvite().getOrElse { false }
|
||||
|
||||
@@ -36,6 +36,7 @@ data class RoomDetailsState(
|
||||
val canShowNotificationSettings: Boolean,
|
||||
val leaveRoomState: LeaveRoomState,
|
||||
val roomNotificationSettings: RoomNotificationSettings?,
|
||||
val isFavorite: Boolean,
|
||||
val eventSink: (RoomDetailsEvent) -> Unit
|
||||
)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState>
|
||||
aDmRoomDetailsState().copy(roomName = "Daniel"),
|
||||
aDmRoomDetailsState(isDmMemberIgnored = true).copy(roomName = "Daniel"),
|
||||
aRoomDetailsState().copy(canInvite = true),
|
||||
aRoomDetailsState().copy(isFavorite = true),
|
||||
aRoomDetailsState().copy(
|
||||
canEdit = true,
|
||||
// Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed
|
||||
@@ -86,6 +87,7 @@ fun aRoomDetailsState() = RoomDetailsState(
|
||||
roomMemberDetailsState = null,
|
||||
leaveRoomState = aLeaveRoomState(),
|
||||
roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false),
|
||||
isFavorite = false,
|
||||
eventSink = {}
|
||||
)
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.components.button.MainActionButton
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
@@ -163,6 +164,13 @@ fun RoomDetailsView(
|
||||
)
|
||||
}
|
||||
|
||||
FavoriteSection(
|
||||
isFavorite = state.isFavorite,
|
||||
onFavoriteChanges = {
|
||||
state.eventSink(RoomDetailsEvent.SetIsFavorite(it))
|
||||
}
|
||||
)
|
||||
|
||||
if (state.roomType is RoomDetailsType.Room) {
|
||||
MembersSection(
|
||||
memberCount = state.memberCount,
|
||||
@@ -355,6 +363,22 @@ private fun NotificationSection(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FavoriteSection(
|
||||
isFavorite: Boolean,
|
||||
onFavoriteChanges: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
PreferenceCategory(modifier = modifier) {
|
||||
PreferenceSwitch(
|
||||
icon = CompoundIcons.FavouriteOff,
|
||||
title = "Favorite",
|
||||
isChecked = isFavorite,
|
||||
onCheckedChange = onFavoriteChanges
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MembersSection(
|
||||
memberCount: Long,
|
||||
|
||||
@@ -26,6 +26,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
@@ -56,7 +58,10 @@ fun RoomListContextMenu(
|
||||
onLeaveRoomClicked = {
|
||||
eventSink(RoomListEvents.HideContextMenu)
|
||||
eventSink(RoomListEvents.LeaveRoom(contextMenu.roomId))
|
||||
}
|
||||
},
|
||||
onFavoriteChanged = { isFavorite ->
|
||||
eventSink(RoomListEvents.MarkRoomAsFavorite(contextMenu.roomId, isFavorite))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -66,6 +71,7 @@ private fun RoomListModalBottomSheetContent(
|
||||
contextMenu: RoomListState.ContextMenu.Shown,
|
||||
onRoomSettingsClicked: (roomId: RoomId) -> Unit,
|
||||
onLeaveRoomClicked: (roomId: RoomId) -> Unit,
|
||||
onFavoriteChanged: (isFavorite: Boolean) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -78,6 +84,31 @@ private fun RoomListModalBottomSheetContent(
|
||||
)
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = "Favourite",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
},
|
||||
leadingContent = ListItemContent.Icon(
|
||||
iconSource = IconSource.Vector(
|
||||
CompoundIcons.FavouriteOff,
|
||||
contentDescription = "Favourite"
|
||||
)
|
||||
),
|
||||
trailingContent = ListItemContent.Switch(
|
||||
checked = contextMenu.isFavorite.dataOrNull().orFalse(),
|
||||
enabled = contextMenu.isFavorite is AsyncData.Success,
|
||||
onChange = { isFavorite ->
|
||||
onFavoriteChanged(isFavorite)
|
||||
},
|
||||
),
|
||||
onClick = {
|
||||
onFavoriteChanged(!contextMenu.isFavorite.dataOrNull().orFalse())
|
||||
},
|
||||
style = ListItemStyle.Primary,
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
@@ -96,11 +127,13 @@ private fun RoomListModalBottomSheetContent(
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
val leaveText = stringResource(id = if (contextMenu.isDm) {
|
||||
CommonStrings.action_leave_conversation
|
||||
} else {
|
||||
CommonStrings.action_leave_room
|
||||
})
|
||||
val leaveText = stringResource(
|
||||
id = if (contextMenu.isDm) {
|
||||
CommonStrings.action_leave_conversation
|
||||
} else {
|
||||
CommonStrings.action_leave_room
|
||||
}
|
||||
)
|
||||
Text(text = leaveText)
|
||||
},
|
||||
modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) },
|
||||
@@ -126,9 +159,11 @@ internal fun RoomListModalBottomSheetContentPreview() = ElementPreview {
|
||||
roomId = RoomId(value = "!aRoom:aDomain"),
|
||||
roomName = "aRoom",
|
||||
isDm = false,
|
||||
isFavorite = AsyncData.Success(false),
|
||||
),
|
||||
onRoomSettingsClicked = {},
|
||||
onLeaveRoomClicked = {}
|
||||
onLeaveRoomClicked = {},
|
||||
onFavoriteChanged = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,8 +175,10 @@ internal fun RoomListModalBottomSheetContentForDmPreview() = ElementPreview {
|
||||
roomId = RoomId(value = "!aRoom:aDomain"),
|
||||
roomName = "aRoom",
|
||||
isDm = true,
|
||||
isFavorite = AsyncData.Success(false),
|
||||
),
|
||||
onRoomSettingsClicked = {},
|
||||
onLeaveRoomClicked = {}
|
||||
onLeaveRoomClicked = {},
|
||||
onFavoriteChanged = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,4 +28,5 @@ sealed interface RoomListEvents {
|
||||
data class ShowContextMenu(val roomListRoomSummary: RoomListRoomSummary) : RoomListEvents
|
||||
data object HideContextMenu : RoomListEvents
|
||||
data class LeaveRoom(val roomId: RoomId) : RoomListEvents
|
||||
data class MarkRoomAsFavorite(val roomId: RoomId, val isFavorite: Boolean) : RoomListEvents
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
@@ -32,7 +33,10 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.coroutine.cancel
|
||||
import io.element.android.libraries.architecture.coroutine.rememberJob
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
@@ -41,11 +45,17 @@ import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val EXTENDED_RANGE_SIZE = 40
|
||||
@@ -72,12 +82,12 @@ class RoomListPresenter @Inject constructor(
|
||||
val filteredRoomList by roomListDataSource.filteredRooms.collectAsState()
|
||||
val filter by roomListDataSource.filter.collectAsState()
|
||||
val networkConnectionStatus by networkMonitor.connectivity.collectAsState()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
roomListDataSource.launchIn(this)
|
||||
initialLoad(matrixUser)
|
||||
}
|
||||
|
||||
// Session verification status (unknown, not verified, verified)
|
||||
val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
|
||||
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -101,8 +111,8 @@ class RoomListPresenter @Inject constructor(
|
||||
val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator()
|
||||
|
||||
var displaySearchResults by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
var contextMenu by remember { mutableStateOf<RoomListState.ContextMenu>(RoomListState.ContextMenu.Hidden) }
|
||||
val contextMenu = remember { mutableStateOf<RoomListState.ContextMenu>(RoomListState.ContextMenu.Hidden) }
|
||||
val showContextMenuJob = rememberJob()
|
||||
|
||||
fun handleEvents(event: RoomListEvents) {
|
||||
when (event) {
|
||||
@@ -117,14 +127,14 @@ class RoomListPresenter @Inject constructor(
|
||||
displaySearchResults = !displaySearchResults
|
||||
}
|
||||
is RoomListEvents.ShowContextMenu -> {
|
||||
contextMenu = RoomListState.ContextMenu.Shown(
|
||||
roomId = event.roomListRoomSummary.roomId,
|
||||
roomName = event.roomListRoomSummary.name,
|
||||
isDm = event.roomListRoomSummary.isDm,
|
||||
)
|
||||
showContextMenuJob.value = coroutineScope.showContextMenu(event, contextMenu)
|
||||
}
|
||||
is RoomListEvents.HideContextMenu -> {
|
||||
showContextMenuJob.cancel()
|
||||
contextMenu.value = RoomListState.ContextMenu.Hidden
|
||||
}
|
||||
is RoomListEvents.HideContextMenu -> contextMenu = RoomListState.ContextMenu.Hidden
|
||||
is RoomListEvents.LeaveRoom -> leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(event.roomId))
|
||||
is RoomListEvents.MarkRoomAsFavorite -> coroutineScope.markRoomAsFavorite(event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +152,7 @@ class RoomListPresenter @Inject constructor(
|
||||
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
|
||||
invitesState = inviteStateDataSource.inviteState(),
|
||||
displaySearchResults = displaySearchResults,
|
||||
contextMenu = contextMenu,
|
||||
contextMenu = contextMenu.value,
|
||||
leaveRoomState = leaveRoomState,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
@@ -152,6 +162,38 @@ class RoomListPresenter @Inject constructor(
|
||||
matrixUser.value = client.getCurrentUser()
|
||||
}
|
||||
|
||||
private fun CoroutineScope.showContextMenu(event: RoomListEvents.ShowContextMenu, contextMenuState: MutableState<RoomListState.ContextMenu>) = launch {
|
||||
val initialState = RoomListState.ContextMenu.Shown(
|
||||
roomId = event.roomListRoomSummary.roomId,
|
||||
roomName = event.roomListRoomSummary.name,
|
||||
isDm = event.roomListRoomSummary.isDm,
|
||||
isFavorite = AsyncData.Loading(),
|
||||
)
|
||||
contextMenuState.value = initialState
|
||||
val room = client.getRoom(event.roomListRoomSummary.roomId)
|
||||
if (room != null) {
|
||||
room.notableTagsFlow
|
||||
.distinctUntilChanged()
|
||||
.onEach { tags ->
|
||||
val newState = initialState.copy(isFavorite = AsyncData.Success(tags.isFavorite))
|
||||
contextMenuState.value = newState
|
||||
}
|
||||
.launchIn(this)
|
||||
} else {
|
||||
contextMenuState.value = initialState.copy(isFavorite = AsyncData.Failure(IllegalStateException("Room not found")))
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.markRoomAsFavorite(event: RoomListEvents.MarkRoomAsFavorite) = launch {
|
||||
val room = client.getRoom(event.roomId)
|
||||
if (room != null) {
|
||||
val notableTags = RoomNotableTags(isFavorite = event.isFavorite);
|
||||
room.updateNotableTags(notableTags)
|
||||
}else {
|
||||
Timber.w("Room ${event.roomId} not found, can't mark as favorite");
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVisibleRange(range: IntRange) {
|
||||
if (range.isEmpty()) return
|
||||
val midExtendedRangeSize = EXTENDED_RANGE_SIZE / 2
|
||||
@@ -162,3 +204,5 @@ class RoomListPresenter @Inject constructor(
|
||||
client.roomListService.updateAllRoomsVisibleRange(extendedRange)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -47,6 +48,7 @@ data class RoomListState(
|
||||
val roomId: RoomId,
|
||||
val roomName: String,
|
||||
val isDm: Boolean,
|
||||
val isFavorite: AsyncData<Boolean>,
|
||||
) : ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.leaveroom.api.aLeaveRoomState
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
@@ -46,6 +47,7 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
||||
roomId = RoomId("!aRoom:aDomain"),
|
||||
roomName = "A nice room name",
|
||||
isDm = false,
|
||||
isFavorite = AsyncData.Success(true),
|
||||
)
|
||||
),
|
||||
aRoomListState().copy(displayRecoveryKeyPrompt = true),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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 io.element.android.libraries.architecture.coroutine
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@Composable
|
||||
fun rememberJob(): MutableState<Job?> = remember {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
|
||||
fun MutableState<Job?>.cancel(cause: CancellationException? = null) {
|
||||
value?.cancel(cause)
|
||||
value = null
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
@@ -57,6 +58,8 @@ interface MatrixRoom : Closeable {
|
||||
|
||||
val roomInfoFlow: Flow<MatrixRoomInfo>
|
||||
|
||||
val notableTagsFlow: Flow<RoomNotableTags>
|
||||
|
||||
/**
|
||||
* A one-to-one is a room with exactly 2 members.
|
||||
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||
@@ -150,6 +153,8 @@ interface MatrixRoom : Closeable {
|
||||
|
||||
suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit>
|
||||
|
||||
suspend fun updateNotableTags(notableTags: RoomNotableTags): Result<Unit>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.api.room.tags
|
||||
|
||||
data class RoomNotableTags(
|
||||
val isFavorite: Boolean,
|
||||
)
|
||||
@@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
@@ -50,6 +51,7 @@ import io.element.android.libraries.matrix.impl.notificationsettings.RustNotific
|
||||
import io.element.android.libraries.matrix.impl.poll.toInner
|
||||
import io.element.android.libraries.matrix.impl.room.location.toInner
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
||||
import io.element.android.libraries.matrix.impl.room.tags.map
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
@@ -70,6 +72,7 @@ import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
import org.matrix.rustcomponents.sdk.RoomNotableTagsListener
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.WidgetCapabilities
|
||||
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
|
||||
@@ -79,6 +82,7 @@ import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import org.matrix.rustcomponents.sdk.Room as InnerRoom
|
||||
import uniffi.matrix_sdk_base.RoomNotableTags as RustRoomNotableTags
|
||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -111,6 +115,15 @@ class RustMatrixRoom(
|
||||
})
|
||||
}
|
||||
|
||||
override val notableTagsFlow: Flow<RoomNotableTags> = mxCallbackFlow {
|
||||
innerRoom.subscribeToNotableTags(object : RoomNotableTagsListener {
|
||||
override fun call(notableTags: RustRoomNotableTags) {
|
||||
Timber.d("On notable tags update: $notableTags")
|
||||
channel.trySend(notableTags.map())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create a dispatcher for all room methods...
|
||||
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
|
||||
|
||||
@@ -423,6 +436,17 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateNotableTags(notableTags: RoomNotableTags): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
Timber.i("Update notable tags with : $notableTags")
|
||||
innerRoom.updateNotableTags(notableTags.map())
|
||||
}.onFailure {
|
||||
Timber.w("Failed to update notable tags: $it")
|
||||
}.onSuccess {
|
||||
Timber.i("Successfully updated notable tags")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.room.tags
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import uniffi.matrix_sdk_base.RoomNotableTags as RustRoomNotableTags
|
||||
|
||||
fun RustRoomNotableTags.map() = RoomNotableTags(
|
||||
isFavorite = isFavorite
|
||||
)
|
||||
|
||||
fun RoomNotableTags.map() = RustRoomNotableTags(
|
||||
isFavorite = isFavorite
|
||||
)
|
||||
@@ -25,6 +25,7 @@ import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
||||
|
||||
fun create(roomInfo: RoomInfo): RoomSummaryDetails {
|
||||
val latestRoomMessage = roomInfo.latestEvent?.use {
|
||||
roomMessageFactory.create(it)
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSende
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.matrix.rustcomponents.sdk.Reaction
|
||||
@@ -36,7 +37,6 @@ import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo
|
||||
import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails
|
||||
import org.matrix.rustcomponents.sdk.Receipt as RustReceipt
|
||||
import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin
|
||||
|
||||
class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper()) {
|
||||
fun map(eventTimelineItem: RustEventTimelineItem): EventTimelineItem = eventTimelineItem.use {
|
||||
|
||||
@@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
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.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
@@ -113,6 +114,7 @@ class FakeMatrixRoom(
|
||||
private var getWidgetDriverResult: Result<MatrixWidgetDriver> = Result.success(FakeWidgetDriver())
|
||||
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
|
||||
private var canUserJoinCallResult: Result<Boolean> = Result.success(true)
|
||||
private var updateNotableTagsResult = Result.success(Unit)
|
||||
var sendMessageMentions = emptyList<Mention>()
|
||||
val editMessageCalls = mutableListOf<Pair<String, String?>>()
|
||||
private val _typingRecord = mutableListOf<Boolean>()
|
||||
@@ -169,6 +171,9 @@ class FakeMatrixRoom(
|
||||
private val _roomInfoFlow: MutableSharedFlow<MatrixRoomInfo> = MutableSharedFlow(replay = 1)
|
||||
override val roomInfoFlow: Flow<MatrixRoomInfo> = _roomInfoFlow
|
||||
|
||||
private val _notableTagsFlow: MutableSharedFlow<RoomNotableTags> = MutableStateFlow(aRoomNotableTags())
|
||||
override val notableTagsFlow: Flow<RoomNotableTags> = _notableTagsFlow
|
||||
|
||||
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
||||
|
||||
override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> =
|
||||
@@ -374,6 +379,14 @@ class FakeMatrixRoom(
|
||||
return reportContentResult
|
||||
}
|
||||
|
||||
override suspend fun updateNotableTags(notableTags: RoomNotableTags): Result<Unit> {
|
||||
return updateNotableTagsResult.also { result ->
|
||||
if (result.isSuccess) {
|
||||
_notableTagsFlow.emit(notableTags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
@@ -571,6 +584,10 @@ class FakeMatrixRoom(
|
||||
getWidgetDriverResult = result
|
||||
}
|
||||
|
||||
fun givenUpdateNotableTagsResult(result: Result<Unit>) {
|
||||
updateNotableTagsResult = result
|
||||
}
|
||||
|
||||
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
|
||||
_roomInfoFlow.tryEmit(roomInfo)
|
||||
}
|
||||
@@ -610,6 +627,7 @@ fun aRoomInfo(
|
||||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
alternativeAliases: List<String> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
@@ -646,3 +664,9 @@ fun aRoomInfo(
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
|
||||
)
|
||||
|
||||
fun aRoomNotableTags(
|
||||
isFavorite: Boolean = false,
|
||||
) = RoomNotableTags(
|
||||
isFavorite = isFavorite,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user