Merge pull request #4195 from element-hq/feature/bma/snackBar

Fix snack bar not displayed in MediaViewer
This commit is contained in:
Benoit Marty
2025-01-27 09:52:36 +01:00
committed by GitHub
8 changed files with 17 additions and 17 deletions

View File

@@ -45,7 +45,7 @@ class LoggedInEventProcessor @Inject constructor(
observingJob = null
}
private suspend fun displayMessage(message: Int) {
private fun displayMessage(message: Int) {
snackbarDispatcher.post(SnackbarMessage(message))
}
}

View File

@@ -484,7 +484,7 @@ class MessagesPresenter @AssistedInject constructor(
)
}
private suspend fun handleCopyContents(event: TimelineItem.Event) {
private fun handleCopyContents(event: TimelineItem.Event) {
val content = when (event.content) {
is TimelineItemTextBasedContent -> event.content.body
is TimelineItemStateContent -> event.content.body
@@ -496,7 +496,7 @@ class MessagesPresenter @AssistedInject constructor(
}
}
private suspend fun handleCopyCaption(event: TimelineItem.Event) {
private fun handleCopyCaption(event: TimelineItem.Event) {
val content = (event.content as? TimelineItemEventContentWithAttachment)?.caption ?: return
clipboardHelper.copyPlainText(content)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {

View File

@@ -8,7 +8,6 @@
package io.element.android.features.securebackup.impl.setup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
@@ -22,8 +21,6 @@ import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.di.SessionScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ContributesNode(SessionScope::class)
class SecureBackupSetupNode @AssistedInject constructor(
@@ -42,12 +39,11 @@ class SecureBackupSetupNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val coroutineScope = rememberCoroutineScope()
val state = presenter.present()
SecureBackupSetupView(
state = state,
onSuccess = {
coroutineScope.postSuccessSnackbar()
postSuccessSnackbar()
navigateUp()
},
onBackClick = ::navigateUp,
@@ -55,7 +51,7 @@ class SecureBackupSetupNode @AssistedInject constructor(
)
}
private fun CoroutineScope.postSuccessSnackbar() = launch {
private fun postSuccessSnackbar() {
snackbarDispatcher.post(
SnackbarMessage(
messageResId = if (inputs.isChangeRecoveryKeyUserStory) {

View File

@@ -36,7 +36,7 @@ class SnackbarDispatcher {
}
}
suspend fun post(message: SnackbarMessage) {
fun post(message: SnackbarMessage) {
if (snackBarMessageQueue.isEmpty()) {
snackBarMessageQueue.add(message)
if (queueMutex.isLocked) queueMutex.unlock()
@@ -54,7 +54,7 @@ class SnackbarDispatcher {
}
/** Used to provide a [SnackbarDispatcher] to composable functions, it's needed for [rememberSnackbarHostState]. */
val LocalSnackbarDispatcher = compositionLocalOf<SnackbarDispatcher> { SnackbarDispatcher() }
val LocalSnackbarDispatcher = compositionLocalOf { SnackbarDispatcher() }
@Composable
fun SnackbarDispatcher.collectSnackbarMessageAsState(): State<SnackbarMessage?> {
@@ -72,10 +72,10 @@ fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostSt
} ?: return snackbarHostState
val dispatcher = LocalSnackbarDispatcher.current
LaunchedEffect(snackbarMessageText) {
LaunchedEffect(snackbarMessage.id) {
// If the message wasn't already displayed, do it now, and mark it as displayed
// This will prevent the message from appearing in any other active SnackbarHosts
if (snackbarMessage.isDisplayed.getAndSet(true) == false) {
if (snackbarMessage.isDisplayed.getAndSet(true).not()) {
try {
snackbarHostState.showSnackbar(
message = snackbarMessageText,

View File

@@ -10,6 +10,7 @@ package io.element.android.libraries.designsystem.utils.snackbar
import androidx.annotation.StringRes
import androidx.compose.material3.SnackbarDuration
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.random.Random
/**
* A message to be displayed in a [Snackbar].
@@ -17,6 +18,7 @@ import java.util.concurrent.atomic.AtomicBoolean
* @param duration The duration of the message. The default value is [SnackbarDuration.Short].
* @param actionResId The action text to be displayed. The default value is `null`.
* @param isDisplayed Used to track if the current message is already displayed or not.
* @param id The unique identifier of the message. The default value is a random long.
* @param action The action to be performed when the action is clicked.
*/
data class SnackbarMessage(
@@ -24,5 +26,6 @@ data class SnackbarMessage(
val duration: SnackbarDuration = SnackbarDuration.Short,
@StringRes val actionResId: Int? = null,
val isDisplayed: AtomicBoolean = AtomicBoolean(false),
val id: Long = Random.nextLong(),
val action: () -> Unit = {},
)

View File

@@ -86,11 +86,13 @@ class MediaGalleryPresenter @AssistedInject constructor(
mediaGalleryDataSource.deleteItem(event.eventId)
}
is MediaGalleryEvents.SaveOnDisk -> coroutineScope.launch {
mediaBottomSheetState = MediaBottomSheetState.Hidden
groupedMediaItems.dataOrNull().find(event.eventId)?.let {
saveOnDisk(it)
}
}
is MediaGalleryEvents.Share -> coroutineScope.launch {
mediaBottomSheetState = MediaBottomSheetState.Hidden
groupedMediaItems.dataOrNull().find(event.eventId)?.let {
share(it)
}

View File

@@ -44,7 +44,6 @@ class MediaViewerPresenter @AssistedInject constructor(
@Assisted private val dataSource: MediaViewerDataSource,
private val room: MatrixRoom,
private val localMediaActions: LocalMediaActions,
private val snackbarDispatcher: SnackbarDispatcher,
) : Presenter<MediaViewerState> {
@AssistedFactory
interface Factory {
@@ -55,6 +54,9 @@ class MediaViewerPresenter @AssistedInject constructor(
): MediaViewerPresenter
}
// Use a local snackbarDispatcher because this presenter is used in an Overlay Node
private val snackbarDispatcher = SnackbarDispatcher()
@Composable
override fun present(): MediaViewerState {
val coroutineScope = rememberCoroutineScope()

View File

@@ -13,7 +13,6 @@ import android.net.Uri
import app.cash.turbine.ReceiveTurbine
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.room.MatrixRoom
@@ -568,7 +567,6 @@ class MediaViewerPresenterTest {
eventId: EventId? = null,
matrixMediaLoader: FakeMatrixMediaLoader = FakeMatrixMediaLoader(),
localMediaActions: FakeLocalMediaActions = FakeLocalMediaActions(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
mediaGalleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(
startLambda = { },
),
@@ -598,7 +596,6 @@ class MediaViewerPresenterTest {
),
room = room,
localMediaActions = localMediaActions,
snackbarDispatcher = snackbarDispatcher,
)
}
}