diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt index db3d3dfe2d..3e7fc96c68 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt @@ -20,5 +20,5 @@ sealed interface RoomDetailsEvent { data object LeaveRoom : RoomDetailsEvent data object MuteNotification : RoomDetailsEvent data object UnmuteNotification : RoomDetailsEvent - data class SetIsFavorite(val isFavorite: Boolean) : RoomDetailsEvent + data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 52cb7a9d05..2c663bfdde 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -124,7 +124,7 @@ class RoomDetailsPresenter @Inject constructor( client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne) } } - is RoomDetailsEvent.SetIsFavorite -> { + is RoomDetailsEvent.SetFavorite -> { scope.launch { room.setIsFavorite(event.isFavorite) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index df6b969095..efa8e6e321 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -167,7 +167,7 @@ fun RoomDetailsView( FavoriteSection( isFavorite = state.isFavorite, onFavoriteChanges = { - state.eventSink(RoomDetailsEvent.SetIsFavorite(it)) + state.eventSink(RoomDetailsEvent.SetFavorite(it)) } ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 6840a09d1e..1faccce70e 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -438,9 +438,9 @@ class RoomDetailsPresenterTests { val presenter = createRoomDetailsPresenter(room = room) presenter.test { val initialState = awaitItem() - initialState.eventSink(RoomDetailsEvent.SetIsFavorite(true)) + initialState.eventSink(RoomDetailsEvent.SetFavorite(true)) assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true)) - initialState.eventSink(RoomDetailsEvent.SetIsFavorite(false)) + initialState.eventSink(RoomDetailsEvent.SetFavorite(false)) assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false)) cancelAndIgnoreRemainingEvents() } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index ca81dafc28..69f43957bf 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomPresenter import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -38,8 +39,6 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter 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 @@ -53,9 +52,12 @@ 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.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import javax.inject.Inject @@ -120,7 +122,6 @@ class RoomListPresenter @Inject constructor( var displaySearchResults by rememberSaveable { mutableStateOf(false) } val contextMenu = remember { mutableStateOf(RoomListState.ContextMenu.Hidden) } - val showContextMenuJob = rememberJob() fun handleEvents(event: RoomListEvents) { when (event) { @@ -135,10 +136,9 @@ class RoomListPresenter @Inject constructor( displaySearchResults = !displaySearchResults } is RoomListEvents.ShowContextMenu -> { - showContextMenuJob.value = coroutineScope.showContextMenu(event, contextMenu) + coroutineScope.showContextMenu(event, contextMenu) } is RoomListEvents.HideContextMenu -> { - showContextMenuJob.cancel() contextMenu.value = RoomListState.ContextMenu.Hidden } is RoomListEvents.LeaveRoom -> leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(event.roomId)) @@ -192,6 +192,7 @@ class RoomListPresenter @Inject constructor( matrixUser.value = client.getCurrentUser() } + @OptIn(ExperimentalCoroutinesApi::class) private fun CoroutineScope.showContextMenu(event: RoomListEvents.ShowContextMenu, contextMenuState: MutableState) = launch { val initialState = RoomListState.ContextMenu.Shown( roomId = event.roomListRoomSummary.roomId, @@ -202,13 +203,25 @@ class RoomListPresenter @Inject constructor( hasNewContent = event.roomListRoomSummary.hasNewContent ) contextMenuState.value = initialState + client.getRoom(event.roomListRoomSummary.roomId)?.use { room -> + + val isContextMenuShownFlow = snapshotFlow { + contextMenuState.value is RoomListState.ContextMenu.Shown + } + room.roomInfoFlow .onEach { roomInfo -> contextMenuState.value = initialState.copy( isFavorite = roomInfo.isFavorite, ) } + .flatMapLatest { + isContextMenuShownFlow + } + .takeWhile { isContextMenuShown -> + isContextMenuShown + } .collect() } } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/coroutine/Job.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/coroutine/Job.kt deleted file mode 100644 index bf3d4f783e..0000000000 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/coroutine/Job.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 = remember { - mutableStateOf(null) -} - -fun MutableState.cancel(cause: CancellationException? = null) { - value?.cancel(cause) - value = null -}