Add nicer snackbar propagation.

This commit is contained in:
Alex Hart
2025-12-17 09:11:50 -04:00
committed by jeffrey-signal
parent a3e8ca8d33
commit d76eb9a9e4
15 changed files with 669 additions and 96 deletions

View File

@@ -51,6 +51,7 @@ import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -104,6 +105,10 @@ import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity.Co
import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment
import org.thoughtcrime.securesms.components.settings.app.subscription.GooglePayComponent
import org.thoughtcrime.securesms.components.settings.app.subscription.GooglePayRepository
import org.thoughtcrime.securesms.components.snackbars.LocalSnackbarStateConsumerRegistry
import org.thoughtcrime.securesms.components.snackbars.SnackbarHostKey
import org.thoughtcrime.securesms.components.snackbars.SnackbarState
import org.thoughtcrime.securesms.components.snackbars.SnackbarStateConsumerRegistry
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
import org.thoughtcrime.securesms.compose.SignalTheme
@@ -134,13 +139,13 @@ import org.thoughtcrime.securesms.main.MainNavigationListLocation
import org.thoughtcrime.securesms.main.MainNavigationRail
import org.thoughtcrime.securesms.main.MainNavigationViewModel
import org.thoughtcrime.securesms.main.MainSnackbar
import org.thoughtcrime.securesms.main.MainSnackbarHostKey
import org.thoughtcrime.securesms.main.MainToolbar
import org.thoughtcrime.securesms.main.MainToolbarCallback
import org.thoughtcrime.securesms.main.MainToolbarMode
import org.thoughtcrime.securesms.main.MainToolbarState
import org.thoughtcrime.securesms.main.MainToolbarViewModel
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
import org.thoughtcrime.securesms.main.SnackbarState
import org.thoughtcrime.securesms.main.callNavGraphBuilder
import org.thoughtcrime.securesms.main.chatNavGraphBuilder
import org.thoughtcrime.securesms.main.navigateToDetailLocation
@@ -242,6 +247,8 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
private val megaphoneActionController = MainMegaphoneActionController()
private val mainNavigationCallback = MainNavigationCallback()
private val snackbarRegistry = SnackbarStateConsumerRegistry()
override val googlePayRepository: GooglePayRepository by lazy { GooglePayRepository(this) }
override val googlePayResultPublisher: Subject<GooglePayComponent.GooglePayResult> = PublishSubject.create()
@@ -315,10 +322,12 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
this
) { _, bundle ->
if (bundle.getBoolean(CallQualityBottomSheetFragment.REQUEST_KEY, false)) {
mainNavigationViewModel.setSnackbar(
mainNavigationViewModel.snackbarRegistry.emit(
SnackbarState(
message = getString(R.string.CallQualitySheet__thanks_for_your_feedback),
duration = SnackbarDuration.Short
duration = SnackbarDuration.Short,
hostKey = MainSnackbarHostKey.Chat,
fallbackKey = MainSnackbarHostKey.MainChrome
)
)
}
@@ -327,7 +336,6 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
shareDataTimestampViewModel.setTimestampFromActivityCreation(savedInstanceState, intent)
setContent {
val snackbar by mainNavigationViewModel.snackbar.collectAsStateWithLifecycle()
val mainToolbarState by toolbarViewModel.state.collectAsStateWithLifecycle()
val megaphone by mainNavigationViewModel.megaphone.collectAsStateWithLifecycle()
val mainNavigationState by mainNavigationViewModel.mainNavigationState.collectAsStateWithLifecycle()
@@ -367,10 +375,9 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
}
}
val mainBottomChromeState = remember(mainToolbarState.destination, snackbar, mainToolbarState.mode, megaphone) {
val mainBottomChromeState = remember(mainToolbarState.destination, mainToolbarState.mode, megaphone) {
MainBottomChromeState(
destination = mainToolbarState.destination,
snackbarState = snackbar,
mainToolbarMode = mainToolbarState.mode,
megaphoneState = MainMegaphoneState(
megaphone = megaphone,
@@ -547,7 +554,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
snackbarHost = {
if (wrappedNavigator.scaffoldValue.primary == PaneAdaptedValue.Expanded) {
MainSnackbar(
snackbarState = snackbar,
hostKey = SnackbarHostKey.Global,
onDismissed = mainBottomChromeCallback::onSnackbarDismissed,
modifier = Modifier.navigationBarsPadding()
)
@@ -763,34 +770,36 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
private fun MainContainer(content: @Composable BoxWithConstraintsScope.() -> Unit) {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
SignalTheme(isDarkMode = DynamicTheme.isDarkTheme(this)) {
val backgroundColor = if (!windowSizeClass.isSplitPane()) {
MaterialTheme.colorScheme.surface
} else {
SignalTheme.colors.colorSurface1
}
val modifier = when {
windowSizeClass.isSplitPane() -> {
Modifier
.systemBarsPadding()
.displayCutoutPadding()
CompositionLocalProvider(LocalSnackbarStateConsumerRegistry provides mainNavigationViewModel.snackbarRegistry) {
SignalTheme(isDarkMode = DynamicTheme.isDarkTheme(this)) {
val backgroundColor = if (!windowSizeClass.isSplitPane()) {
MaterialTheme.colorScheme.surface
} else {
SignalTheme.colors.colorSurface1
}
else ->
Modifier
.windowInsetsPadding(
WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
.add(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal))
)
}
val modifier = when {
windowSizeClass.isSplitPane() -> {
Modifier
.systemBarsPadding()
.displayCutoutPadding()
}
BoxWithConstraints(
modifier = Modifier
.background(color = backgroundColor)
.then(modifier)
) {
content()
else ->
Modifier
.windowInsetsPadding(
WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
.add(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal))
)
}
BoxWithConstraints(
modifier = Modifier
.background(color = backgroundColor)
.then(modifier)
) {
content()
}
}
}
}
@@ -876,24 +885,26 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
}
if (resultCode == RESULT_OK && requestCode == CreateSvrPinActivity.REQUEST_NEW_PIN) {
mainNavigationViewModel.setSnackbar(SnackbarState(message = getString(R.string.ConfirmKbsPinFragment__pin_created)))
mainNavigationViewModel.snackbarRegistry.emit(SnackbarState(message = getString(R.string.ConfirmKbsPinFragment__pin_created), hostKey = MainSnackbarHostKey.MainChrome))
mainNavigationViewModel.onMegaphoneCompleted(Megaphones.Event.PINS_FOR_ALL)
}
if (resultCode == RESULT_OK && requestCode == UsernameEditFragment.REQUEST_CODE) {
val snackbarString = getString(R.string.ConversationListFragment_username_recovered_toast, SignalStore.account.username)
mainNavigationViewModel.setSnackbar(
mainNavigationViewModel.snackbarRegistry.emit(
SnackbarState(
message = snackbarString
message = snackbarString,
hostKey = MainSnackbarHostKey.MainChrome
)
)
}
if (resultCode == RESULT_OK && requestCode == VerifyBackupKeyActivity.REQUEST_CODE) {
mainNavigationViewModel.setSnackbar(
mainNavigationViewModel.snackbarRegistry.emit(
SnackbarState(
message = getString(R.string.VerifyBackupKey__backup_key_correct),
duration = SnackbarDuration.Short
duration = SnackbarDuration.Short,
hostKey = MainSnackbarHostKey.MainChrome
)
)
mainNavigationViewModel.onMegaphoneSnoozed(Megaphones.Event.VERIFY_BACKUP_KEY)
@@ -1205,9 +1216,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
mainNavigationViewModel.onMegaphoneVisible(megaphone)
}
override fun onSnackbarDismissed() {
mainNavigationViewModel.setSnackbar(null)
}
override fun onSnackbarDismissed() = Unit
}
inner class MainMegaphoneActionController : MegaphoneActionController {
@@ -1220,9 +1229,10 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
}
override fun onMegaphoneToastRequested(string: String) {
mainNavigationViewModel.setSnackbar(
mainNavigationViewModel.snackbarRegistry.emit(
SnackbarState(
message = string
message = string,
hostKey = MainSnackbarHostKey.MainChrome
)
)
}