From 5a81dd8e93c3b817c3629740de588e29d9609307 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:15:03 -0600 Subject: [PATCH 01/33] Add toasts --- .../compose/toast/ToastController.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt new file mode 100644 index 00000000000..e4ca4f5b1c5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -0,0 +1,37 @@ +package com.lagradost.cloudstream4.compose.toast + +import androidx.compose.material3.SnackbarDuration +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow + +enum class ToastType { Info, Success, Warning, Error } + +data class ToastEvent( + val message: String, + val type: ToastType = ToastType.Info, + val duration: SnackbarDuration = SnackbarDuration.Short, + val actionLabel: String? = null, + val onAction: (() -> Unit)? = null, +) + +object ToastController { + private val _events = Channel(Channel.BUFFERED) + internal val events = _events.receiveAsFlow() + + suspend fun show( + message: String, + type: ToastType = ToastType.Info, + duration: SnackbarDuration = SnackbarDuration.Short, + actionLabel: String? = null, + onAction: (() -> Unit)? = null, + ) = _events.send(ToastEvent(message, type, duration, actionLabel, onAction)) + + suspend fun showSuccess(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = + show(message, ToastType.Success, duration) + + suspend fun showWarning(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = + show(message, ToastType.Warning, duration) + + suspend fun showError(message: String, duration: SnackbarDuration = SnackbarDuration.Long) = + show(message, ToastType.Error, duration) +} From df78f07c6029b1fa92b34266c5735486117919c1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:16:54 -0600 Subject: [PATCH 02/33] Add --- .../cloudstream4/compose/toast/ToastHost.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt new file mode 100644 index 00000000000..eb042dd2672 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -0,0 +1,61 @@ +package com.lagradost.cloudstream4.compose.toast + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarData +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult +import androidx.compose.material3.SnackbarVisuals +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.compose.theme.CloudStreamTheme + +internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { + override val message: String = event.message + override val actionLabel: String? = event.actionLabel + override val duration: SnackbarDuration = event.duration + override val withDismissAction: Boolean = event.actionLabel == null +} + +@Composable +internal fun ToastEffectHost(hostState: SnackbarHostState) { + LaunchedEffect(hostState) { + ToastController.events.collect { event -> + val result = hostState.showSnackbar(ToastVisuals(event)) + if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() + } + } +} + +@Composable +private fun ToastType.containerColor(): Color { + val c = CloudStreamTheme.colors + return when (this) { + ToastType.Info -> c.surfaceVariant + ToastType.Success -> c.primary.copy(alpha = 0.90f) + ToastType.Warning -> Color(0xFFB45309) + ToastType.Error -> Color(0xFFB91C1C) + } +} + +@Composable +private fun ToastType.contentColor(): Color = when (this) { + ToastType.Info -> CloudStreamTheme.colors.onSurfaceVariant + else -> Color.White +} + +@Composable +fun CloudStreamSnackbar(data: SnackbarData) { + val type = (data.visuals as? ToastVisuals)?.event?.type ?: ToastType.Info + Snackbar( + snackbarData = data, + shape = RoundedCornerShape(12.dp), + containerColor = type.containerColor(), + contentColor = type.contentColor(), + actionColor = type.contentColor(), + dismissActionContentColor = type.contentColor(), + ) +} From e2d2e279b1c10d09499d9f6a55ee80138189419f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:18:08 -0600 Subject: [PATCH 03/33] Add --- .../compose/theme/CloudStreamTheme.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 03cee9b256f..0f2d774cf64 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -1,11 +1,21 @@ package com.lagradost.cloudstream4.compose.theme import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.compose.toast.CloudStreamSnackbar +import com.lagradost.cloudstream4.compose.toast.ToastEffectHost val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } @@ -73,10 +83,21 @@ fun CloudStreamTheme( } } + val snackbarHostState = remember { SnackbarHostState() } + ToastEffectHost(snackbarHostState) + CompositionLocalProvider(LocalCloudStreamColors provides csColors) { - MaterialTheme( - colorScheme = csColors.toMaterial3ColorScheme(), - content = content, - ) + MaterialTheme(colorScheme = csColors.toMaterial3ColorScheme()) { + Box(modifier = Modifier.fillMaxSize()) { + content() + SnackbarHost( + hostState = snackbarHostState, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), + snackbar = { CloudStreamSnackbar(it) }, + ) + } + } } } From dafbdd05282b9242920906d37ab3d8ae4c35ddd7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:24:11 -0600 Subject: [PATCH 04/33] Add post methods --- .../compose/toast/ToastController.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index e4ca4f5b1c5..c3178f38148 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -18,6 +18,23 @@ object ToastController { private val _events = Channel(Channel.BUFFERED) internal val events = _events.receiveAsFlow() + fun post( + message: String, + type: ToastType = ToastType.Info, + duration: SnackbarDuration = SnackbarDuration.Short, + actionLabel: String? = null, + onAction: (() -> Unit)? = null, + ) { _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction)) } + + fun postSuccess(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = + post(message, ToastType.Success, duration) + + fun postWarning(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = + post(message, ToastType.Warning, duration) + + fun postError(message: String, duration: SnackbarDuration = SnackbarDuration.Long) = + post(message, ToastType.Error, duration) + suspend fun show( message: String, type: ToastType = ToastType.Info, From e436a668bf68b198d3216032d593113ee31a0a0a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:26:16 -0600 Subject: [PATCH 05/33] Test --- .../cloudstream3/ui/compose/settings/SettingsFragment.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt index 5b1f1d1825a..04119bdc356 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt @@ -64,7 +64,8 @@ class SettingsFragment : Fragment() { val v = version.appVersion val h = version.commitHash val d = version.buildDate - clipboardHelper(txt(R.string.extension_version), "$v $h $d") + // clipboardHelper(txt(R.string.extension_version), "$v $h $d") + com.lagradost.cloudstream4.compose.toast.ToastController.post("Test") }, ) } From 79a4d5ec90f50e88eca5acec652165ac5e369364 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:29:26 -0600 Subject: [PATCH 06/33] - --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 675ce3b2f77..bf499d2a77e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -27,7 +27,7 @@ jobs: cache-read-only: false - name: Run Gradle - run: ./gradlew assemblePrereleaseDebug lint + run: ./gradlew assemblePrereleaseDebug - name: Upload Artifact uses: actions/upload-artifact@v7 From 096cda95214185a568cf05295a5906fee345392f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:38:14 -0600 Subject: [PATCH 07/33] Try --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index eb042dd2672..2ab4a926b33 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -34,7 +34,7 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { private fun ToastType.containerColor(): Color { val c = CloudStreamTheme.colors return when (this) { - ToastType.Info -> c.surfaceVariant + ToastType.Info -> c.surface ToastType.Success -> c.primary.copy(alpha = 0.90f) ToastType.Warning -> Color(0xFFB45309) ToastType.Error -> Color(0xFFB91C1C) @@ -43,7 +43,7 @@ private fun ToastType.containerColor(): Color { @Composable private fun ToastType.contentColor(): Color = when (this) { - ToastType.Info -> CloudStreamTheme.colors.onSurfaceVariant + ToastType.Info -> CloudStreamTheme.colors.onSurface else -> Color.White } From 3f17a888d3707193dcfee08910fdb45979a38a64 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:42:27 -0600 Subject: [PATCH 08/33] Update --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index 2ab4a926b33..d52c54cd31c 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -34,7 +34,7 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { private fun ToastType.containerColor(): Color { val c = CloudStreamTheme.colors return when (this) { - ToastType.Info -> c.surface + ToastType.Info -> c.background ToastType.Success -> c.primary.copy(alpha = 0.90f) ToastType.Warning -> Color(0xFFB45309) ToastType.Error -> Color(0xFFB91C1C) @@ -43,7 +43,7 @@ private fun ToastType.containerColor(): Color { @Composable private fun ToastType.contentColor(): Color = when (this) { - ToastType.Info -> CloudStreamTheme.colors.onSurface + ToastType.Info -> CloudStreamTheme.colors.onBackground else -> Color.White } From bea28386b117ec375605dd21dcc51b965c1154a3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:48:46 -0600 Subject: [PATCH 09/33] test --- .../cloudstream3/ui/compose/settings/SettingsFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt index 04119bdc356..cccd28ec467 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt @@ -65,7 +65,10 @@ class SettingsFragment : Fragment() { val h = version.commitHash val d = version.buildDate // clipboardHelper(txt(R.string.extension_version), "$v $h $d") - com.lagradost.cloudstream4.compose.toast.ToastController.post("Test") + com.lagradost.cloudstream4.compose.toast.ToastController.post("Test default") + com.lagradost.cloudstream4.compose.toast.ToastController.postSuccess("Test success") + com.lagradost.cloudstream4.compose.toast.ToastController.postError("Test error") + com.lagradost.cloudstream4.compose.toast.ToastController.postWarning("Test warning") }, ) } From 31d136e048948cdd72b479d3283135b7299f574e Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:57:19 -0600 Subject: [PATCH 10/33] Update --- .../compose/toast/ToastController.kt | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index c3178f38148..bc6c0011831 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -12,6 +12,8 @@ data class ToastEvent( val duration: SnackbarDuration = SnackbarDuration.Short, val actionLabel: String? = null, val onAction: (() -> Unit)? = null, + val dismissable: Boolean = false, + val queue: Boolean = false, ) object ToastController { @@ -24,16 +26,33 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, actionLabel: String? = null, onAction: (() -> Unit)? = null, - ) { _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction)) } + dismissable: Boolean = false, + queue: Boolean = false, + ) { + if (!queue) _events.tryReceive() + _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) + } - fun postSuccess(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = - post(message, ToastType.Success, duration) + fun postSuccess( + message: String, + duration: SnackbarDuration = SnackbarDuration.Short, + dismissable: Boolean = false, + queue: Boolean = false, + ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) - fun postWarning(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = - post(message, ToastType.Warning, duration) + fun postWarning( + message: String, + duration: SnackbarDuration = SnackbarDuration.Short, + dismissable: Boolean = false, + queue: Boolean = false, + ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) - fun postError(message: String, duration: SnackbarDuration = SnackbarDuration.Long) = - post(message, ToastType.Error, duration) + fun postError( + message: String, + duration: SnackbarDuration = SnackbarDuration.Long, + dismissable: Boolean = false, + queue: Boolean = false, + ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) suspend fun show( message: String, @@ -41,14 +60,31 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, actionLabel: String? = null, onAction: (() -> Unit)? = null, - ) = _events.send(ToastEvent(message, type, duration, actionLabel, onAction)) + dismissable: Boolean = false, + queue: Boolean = false, + ) { + if (!queue) _events.tryReceive() + _events.send(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) + } - suspend fun showSuccess(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = - show(message, ToastType.Success, duration) + suspend fun showSuccess( + message: String, + duration: SnackbarDuration = SnackbarDuration.Short, + dismissable: Boolean = false, + queue: Boolean = false, + ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) - suspend fun showWarning(message: String, duration: SnackbarDuration = SnackbarDuration.Short) = - show(message, ToastType.Warning, duration) + suspend fun showWarning( + message: String, + duration: SnackbarDuration = SnackbarDuration.Short, + dismissable: Boolean = false, + queue: Boolean = false, + ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) - suspend fun showError(message: String, duration: SnackbarDuration = SnackbarDuration.Long) = - show(message, ToastType.Error, duration) + suspend fun showError( + message: String, + duration: SnackbarDuration = SnackbarDuration.Long, + dismissable: Boolean = false, + queue: Boolean = false, + ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) } From 3b7cd85c1e33c334e5af184aee880dcde65809e8 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 15:59:07 -0600 Subject: [PATCH 11/33] Update --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index d52c54cd31c..ba4e64dd607 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -17,7 +17,7 @@ internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { override val message: String = event.message override val actionLabel: String? = event.actionLabel override val duration: SnackbarDuration = event.duration - override val withDismissAction: Boolean = event.actionLabel == null + override val withDismissAction: Boolean = event.dismissable } @Composable From bc2de089298ae6d66859664bea92285034ac3a32 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:08:56 -0600 Subject: [PATCH 12/33] Fix nested --- .../compose/theme/CloudStreamTheme.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 0f2d774cf64..3bdcee69d94 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -18,6 +18,7 @@ import com.lagradost.cloudstream4.compose.toast.CloudStreamSnackbar import com.lagradost.cloudstream4.compose.toast.ToastEffectHost val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } +val LocalSnackbarHostState = compositionLocalOf { null } object CloudStreamTheme { val colors: CloudStreamColorScheme @@ -60,7 +61,6 @@ fun CloudStreamTheme( ) { val systemDark = isSystemInDarkTheme() val dynamicTheme = resolveDynamicTheme() - val dynamicPrimary = resolveDynamicPrimaryColor() val dynamicSecondary = resolveDynamicSecondaryColor() @@ -83,20 +83,28 @@ fun CloudStreamTheme( } } - val snackbarHostState = remember { SnackbarHostState() } - ToastEffectHost(snackbarHostState) + val parentHostState = LocalSnackbarHostState.current + val isRoot = parentHostState == null + val hostState = remember { parentHostState ?: SnackbarHostState() } + + if (isRoot) ToastEffectHost(hostState) - CompositionLocalProvider(LocalCloudStreamColors provides csColors) { + CompositionLocalProvider( + LocalCloudStreamColors provides csColors, + LocalSnackbarHostState provides hostState, + ) { MaterialTheme(colorScheme = csColors.toMaterial3ColorScheme()) { Box(modifier = Modifier.fillMaxSize()) { content() - SnackbarHost( - hostState = snackbarHostState, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), - snackbar = { CloudStreamSnackbar(it) }, - ) + if (isRoot) { + SnackbarHost( + hostState = hostState, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), + snackbar = { CloudStreamSnackbar(it) }, + ) + } } } } From ca68b9ffbc310adf4c2ff6940aa7b5d2f40a00d5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:13:10 -0600 Subject: [PATCH 13/33] Fix --- .../kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index ba4e64dd607..86ef02504ad 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -24,6 +24,7 @@ internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { internal fun ToastEffectHost(hostState: SnackbarHostState) { LaunchedEffect(hostState) { ToastController.events.collect { event -> + if (!event.queue) hostState.currentSnackbarData?.dismiss() val result = hostState.showSnackbar(ToastVisuals(event)) if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() } From 42c573149762a0a7d709ecae410e9ca2e204829a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:18:18 -0600 Subject: [PATCH 14/33] add drain --- .../com/lagradost/cloudstream4/compose/toast/ToastController.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index bc6c0011831..cc38adc99ff 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -20,6 +20,8 @@ object ToastController { private val _events = Channel(Channel.BUFFERED) internal val events = _events.receiveAsFlow() + internal fun drain(): ToastEvent? = _events.tryReceive().getOrNull() + fun post( message: String, type: ToastType = ToastType.Info, From 714e540d41a2e16b8351b7f04abdf34b744b309f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:19:53 -0600 Subject: [PATCH 15/33] Fix toast-like snackbar --- .../cloudstream4/compose/toast/ToastHost.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index 86ef02504ad..b3a6075c6f0 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -24,9 +24,20 @@ internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { internal fun ToastEffectHost(hostState: SnackbarHostState) { LaunchedEffect(hostState) { ToastController.events.collect { event -> - if (!event.queue) hostState.currentSnackbarData?.dismiss() - val result = hostState.showSnackbar(ToastVisuals(event)) - if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() + if (!event.queue) { + hostState.currentSnackbarData?.dismiss() + var latest = event + var next = ToastController.drain() + while (next != null) { + latest = next + next = ToastController.drain() + } + val result = hostState.showSnackbar(ToastVisuals(latest)) + if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() + } else { + val result = hostState.showSnackbar(ToastVisuals(event)) + if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() + } } } } From 17b87d7d9ecd468f0aeb81b4c4015d9f1c9be331 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:21:25 -0600 Subject: [PATCH 16/33] Fix position --- .../cloudstream4/compose/theme/CloudStreamTheme.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 3bdcee69d94..6a3426ebdfd 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -2,8 +2,12 @@ package com.lagradost.cloudstream4.compose.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -13,7 +17,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp import com.lagradost.cloudstream4.compose.toast.CloudStreamSnackbar import com.lagradost.cloudstream4.compose.toast.ToastEffectHost @@ -101,7 +104,7 @@ fun CloudStreamTheme( hostState = hostState, modifier = Modifier .align(Alignment.BottomCenter) - .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), + .windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom)), snackbar = { CloudStreamSnackbar(it) }, ) } From 189804008594c20c191181de8ad7826aa9a8b80b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:23:11 -0600 Subject: [PATCH 17/33] Add wrapWidth --- .../compose/toast/ToastController.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index cc38adc99ff..3e0d22b03e3 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -14,6 +14,7 @@ data class ToastEvent( val onAction: (() -> Unit)? = null, val dismissable: Boolean = false, val queue: Boolean = false, + val wrapWidth: Boolean = true, ) object ToastController { @@ -30,9 +31,10 @@ object ToastController { onAction: (() -> Unit)? = null, dismissable: Boolean = false, queue: Boolean = false, + wrapWidth: Boolean = true, ) { if (!queue) _events.tryReceive() - _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) + _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue, wrapWidth)) } fun postSuccess( @@ -40,21 +42,24 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) fun postWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) fun postError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, dismissable: Boolean = false, queue: Boolean = false, - ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) suspend fun show( message: String, @@ -64,9 +69,10 @@ object ToastController { onAction: (() -> Unit)? = null, dismissable: Boolean = false, queue: Boolean = false, + wrapWidth: Boolean = true, ) { if (!queue) _events.tryReceive() - _events.send(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) + _events.send(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue, wrapWidth)) } suspend fun showSuccess( @@ -74,19 +80,22 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) suspend fun showWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) suspend fun showError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, dismissable: Boolean = false, queue: Boolean = false, - ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) + wrapWidth: Boolean = true, + ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) } From 74188db6ecf56e9c20ac0bb95ea0b3b9f079eb30 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:23:46 -0600 Subject: [PATCH 18/33] wrapWidth --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index b3a6075c6f0..eb71f9c4f78 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -1,5 +1,7 @@ package com.lagradost.cloudstream4.compose.toast +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Snackbar import androidx.compose.material3.SnackbarData @@ -9,6 +11,7 @@ import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarVisuals import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.lagradost.cloudstream4.compose.theme.CloudStreamTheme @@ -61,7 +64,9 @@ private fun ToastType.contentColor(): Color = when (this) { @Composable fun CloudStreamSnackbar(data: SnackbarData) { - val type = (data.visuals as? ToastVisuals)?.event?.type ?: ToastType.Info + val event = (data.visuals as? ToastVisuals)?.event + val type = event?.type ?: ToastType.Info + val wrapWidth = event?.wrapWidth ?: true Snackbar( snackbarData = data, shape = RoundedCornerShape(12.dp), @@ -69,5 +74,6 @@ fun CloudStreamSnackbar(data: SnackbarData) { contentColor = type.contentColor(), actionColor = type.contentColor(), dismissActionContentColor = type.contentColor(), + modifier = if (wrapWidth) Modifier.wrapContentWidth() else Modifier.fillMaxWidth(), ) } From fb7126142bc4662147f95f1aef56f3822fc4d4aa Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:30:52 -0600 Subject: [PATCH 19/33] Try --- .../cloudstream4/compose/theme/CloudStreamTheme.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 6a3426ebdfd..2e87ceb6977 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -2,12 +2,7 @@ package com.lagradost.cloudstream4.compose.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -104,7 +99,6 @@ fun CloudStreamTheme( hostState = hostState, modifier = Modifier .align(Alignment.BottomCenter) - .windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom)), snackbar = { CloudStreamSnackbar(it) }, ) } From e6f57b804b9b37903e0883533285c759301ecb90 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:34:31 -0600 Subject: [PATCH 20/33] Fix --- .../lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 2e87ceb6977..b3ca84f18d0 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -97,8 +97,7 @@ fun CloudStreamTheme( if (isRoot) { SnackbarHost( hostState = hostState, - modifier = Modifier - .align(Alignment.BottomCenter) + modifier = Modifier.align(Alignment.BottomCenter), snackbar = { CloudStreamSnackbar(it) }, ) } From f1f293c97e245e917f1a6e859d47bb0b8b88f12a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:43:08 -0600 Subject: [PATCH 21/33] Fix no queue --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index eb71f9c4f78..e528a4b42ee 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -26,6 +26,7 @@ internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { @Composable internal fun ToastEffectHost(hostState: SnackbarHostState) { LaunchedEffect(hostState) { + var lastMessage: String? = null ToastController.events.collect { event -> if (!event.queue) { hostState.currentSnackbarData?.dismiss() @@ -35,8 +36,11 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { latest = next next = ToastController.drain() } + if (latest.message == lastMessage) return@collect + lastMessage = latest.message val result = hostState.showSnackbar(ToastVisuals(latest)) if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() + lastMessage = null } else { val result = hostState.showSnackbar(ToastVisuals(event)) if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() From f6bd1558115a077fae0bffa1919186fb1eb16dc5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:45:52 -0600 Subject: [PATCH 22/33] Fix wrapWidth --- .../cloudstream4/compose/theme/CloudStreamTheme.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index b3ca84f18d0..2256170ce40 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -3,6 +3,8 @@ package com.lagradost.cloudstream4.compose.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -14,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.lagradost.cloudstream4.compose.toast.CloudStreamSnackbar import com.lagradost.cloudstream4.compose.toast.ToastEffectHost +import com.lagradost.cloudstream4.compose.toast.ToastVisuals val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } val LocalSnackbarHostState = compositionLocalOf { null } @@ -95,9 +98,12 @@ fun CloudStreamTheme( Box(modifier = Modifier.fillMaxSize()) { content() if (isRoot) { + val wrapWidth = (hostState.currentSnackbarData?.visuals as? ToastVisuals)?.event?.wrapWidth ?: true SnackbarHost( hostState = hostState, - modifier = Modifier.align(Alignment.BottomCenter), + modifier = Modifier + .align(Alignment.BottomCenter) + .then(if (wrapWidth) Modifier.wrapContentWidth() else Modifier.fillMaxWidth()), snackbar = { CloudStreamSnackbar(it) }, ) } From f5ea49d0b26da3265fbebc0cee6db7143f6518b0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:50:39 -0600 Subject: [PATCH 23/33] Fix queue issue --- .../cloudstream4/compose/toast/ToastHost.kt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index e528a4b42ee..0cf5c659f07 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -1,7 +1,5 @@ package com.lagradost.cloudstream4.compose.toast -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Snackbar import androidx.compose.material3.SnackbarData @@ -11,7 +9,6 @@ import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarVisuals import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.lagradost.cloudstream4.compose.theme.CloudStreamTheme @@ -26,7 +23,7 @@ internal class ToastVisuals(val event: ToastEvent) : SnackbarVisuals { @Composable internal fun ToastEffectHost(hostState: SnackbarHostState) { LaunchedEffect(hostState) { - var lastMessage: String? = null + var showingMessage: String? = null ToastController.events.collect { event -> if (!event.queue) { hostState.currentSnackbarData?.dismiss() @@ -36,11 +33,11 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { latest = next next = ToastController.drain() } - if (latest.message == lastMessage) return@collect - lastMessage = latest.message + if (latest.message == showingMessage) return@collect + showingMessage = latest.message val result = hostState.showSnackbar(ToastVisuals(latest)) if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() - lastMessage = null + showingMessage = null } else { val result = hostState.showSnackbar(ToastVisuals(event)) if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() @@ -70,7 +67,6 @@ private fun ToastType.contentColor(): Color = when (this) { fun CloudStreamSnackbar(data: SnackbarData) { val event = (data.visuals as? ToastVisuals)?.event val type = event?.type ?: ToastType.Info - val wrapWidth = event?.wrapWidth ?: true Snackbar( snackbarData = data, shape = RoundedCornerShape(12.dp), @@ -78,6 +74,5 @@ fun CloudStreamSnackbar(data: SnackbarData) { contentColor = type.contentColor(), actionColor = type.contentColor(), dismissActionContentColor = type.contentColor(), - modifier = if (wrapWidth) Modifier.wrapContentWidth() else Modifier.fillMaxWidth(), ) } From b963f226e3b0a7a5d12fe9f63785249e2d7b486b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:52:56 -0600 Subject: [PATCH 24/33] Try fix wrapWidth --- .../compose/theme/CloudStreamTheme.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 2256170ce40..25c9cc8c0db 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -98,14 +98,23 @@ fun CloudStreamTheme( Box(modifier = Modifier.fillMaxSize()) { content() if (isRoot) { - val wrapWidth = (hostState.currentSnackbarData?.visuals as? ToastVisuals)?.event?.wrapWidth ?: true - SnackbarHost( - hostState = hostState, - modifier = Modifier - .align(Alignment.BottomCenter) - .then(if (wrapWidth) Modifier.wrapContentWidth() else Modifier.fillMaxWidth()), - snackbar = { CloudStreamSnackbar(it) }, - ) + val currentData = hostState.currentSnackbarData + val wrapWidth = (currentData?.visuals as? ToastVisuals)?.event?.wrapWidth ?: true + if (wrapWidth && currentData != null) { + Box( + modifier = Modifier + .align(Alignment.BottomCenter) + .wrapContentWidth() + ) { + CloudStreamSnackbar(currentData) + } + } else { + SnackbarHost( + hostState = hostState, + modifier = Modifier.align(Alignment.BottomCenter), + snackbar = { CloudStreamSnackbar(it) }, + ) + } } } } From 4fcf59832aa8af9127bf28e77960ddd7c7a023ee Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:57:11 -0600 Subject: [PATCH 25/33] Drop wrapWidth --- .../compose/toast/ToastController.kt | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index 3e0d22b03e3..cc38adc99ff 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -14,7 +14,6 @@ data class ToastEvent( val onAction: (() -> Unit)? = null, val dismissable: Boolean = false, val queue: Boolean = false, - val wrapWidth: Boolean = true, ) object ToastController { @@ -31,10 +30,9 @@ object ToastController { onAction: (() -> Unit)? = null, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, ) { if (!queue) _events.tryReceive() - _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue, wrapWidth)) + _events.trySend(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) } fun postSuccess( @@ -42,24 +40,21 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) fun postWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) fun postError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) suspend fun show( message: String, @@ -69,10 +64,9 @@ object ToastController { onAction: (() -> Unit)? = null, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, ) { if (!queue) _events.tryReceive() - _events.send(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue, wrapWidth)) + _events.send(ToastEvent(message, type, duration, actionLabel, onAction, dismissable, queue)) } suspend fun showSuccess( @@ -80,22 +74,19 @@ object ToastController { duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) suspend fun showWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) suspend fun showError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, dismissable: Boolean = false, queue: Boolean = false, - wrapWidth: Boolean = true, - ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue, wrapWidth = wrapWidth) + ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) } From 3d0d452ca93258336b980b5dae4cc23bcfbe0862 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:57:48 -0600 Subject: [PATCH 26/33] - --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index 0cf5c659f07..5f759782c4e 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -65,8 +65,7 @@ private fun ToastType.contentColor(): Color = when (this) { @Composable fun CloudStreamSnackbar(data: SnackbarData) { - val event = (data.visuals as? ToastVisuals)?.event - val type = event?.type ?: ToastType.Info + val type = (data.visuals as? ToastVisuals)?.event?.type ?: ToastType.Info Snackbar( snackbarData = data, shape = RoundedCornerShape(12.dp), From 50940c15e0fc414e9cf2a14b670aa09c664b20c7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 16:58:33 -0600 Subject: [PATCH 27/33] Drop wrapWidth --- .../compose/theme/CloudStreamTheme.kt | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt index 25c9cc8c0db..b3ca84f18d0 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/theme/CloudStreamTheme.kt @@ -3,8 +3,6 @@ package com.lagradost.cloudstream4.compose.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -16,7 +14,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.lagradost.cloudstream4.compose.toast.CloudStreamSnackbar import com.lagradost.cloudstream4.compose.toast.ToastEffectHost -import com.lagradost.cloudstream4.compose.toast.ToastVisuals val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } val LocalSnackbarHostState = compositionLocalOf { null } @@ -98,23 +95,11 @@ fun CloudStreamTheme( Box(modifier = Modifier.fillMaxSize()) { content() if (isRoot) { - val currentData = hostState.currentSnackbarData - val wrapWidth = (currentData?.visuals as? ToastVisuals)?.event?.wrapWidth ?: true - if (wrapWidth && currentData != null) { - Box( - modifier = Modifier - .align(Alignment.BottomCenter) - .wrapContentWidth() - ) { - CloudStreamSnackbar(currentData) - } - } else { - SnackbarHost( - hostState = hostState, - modifier = Modifier.align(Alignment.BottomCenter), - snackbar = { CloudStreamSnackbar(it) }, - ) - } + SnackbarHost( + hostState = hostState, + modifier = Modifier.align(Alignment.BottomCenter), + snackbar = { CloudStreamSnackbar(it) }, + ) } } } From 739364ff49acdd68f805490ffc72595c7a6c6115 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:02:41 -0600 Subject: [PATCH 28/33] call drain --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index 5f759782c4e..a549c996c2c 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -37,8 +37,10 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { showingMessage = latest.message val result = hostState.showSnackbar(ToastVisuals(latest)) if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() + ToastController.drain() showingMessage = null } else { + showingMessage = null val result = hostState.showSnackbar(ToastVisuals(event)) if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() } From 49924ac39dddc0d8ed6c4138f0c03ad00d9979ce Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:06:54 -0600 Subject: [PATCH 29/33] Update defaults --- .../compose/toast/ToastController.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt index cc38adc99ff..05a1f331ce9 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastController.kt @@ -38,22 +38,22 @@ object ToastController { fun postSuccess( message: String, duration: SnackbarDuration = SnackbarDuration.Short, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = post(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) fun postWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = post(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) fun postError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = post(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) suspend fun show( @@ -72,21 +72,21 @@ object ToastController { suspend fun showSuccess( message: String, duration: SnackbarDuration = SnackbarDuration.Short, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = show(message, ToastType.Success, duration, dismissable = dismissable, queue = queue) suspend fun showWarning( message: String, duration: SnackbarDuration = SnackbarDuration.Short, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = show(message, ToastType.Warning, duration, dismissable = dismissable, queue = queue) suspend fun showError( message: String, duration: SnackbarDuration = SnackbarDuration.Long, - dismissable: Boolean = false, - queue: Boolean = false, + dismissable: Boolean = true, + queue: Boolean = true, ) = show(message, ToastType.Error, duration, dismissable = dismissable, queue = queue) } From c4c13b60c51dc5859133205e9ba06cfe6157b7b5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:13:34 -0600 Subject: [PATCH 30/33] Fix mixed queue no queue --- .../cloudstream4/compose/toast/ToastHost.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index a549c996c2c..b596aaeb4d6 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -28,19 +28,24 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { if (!event.queue) { hostState.currentSnackbarData?.dismiss() var latest = event + val queued = mutableListOf() var next = ToastController.drain() while (next != null) { - latest = next + if (next.queue) queued.add(next) + else latest = next next = ToastController.drain() } - if (latest.message == showingMessage) return@collect - showingMessage = latest.message - val result = hostState.showSnackbar(ToastVisuals(latest)) - if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() - ToastController.drain() - showingMessage = null + if (latest.message != showingMessage) { + showingMessage = latest.message + val result = hostState.showSnackbar(ToastVisuals(latest)) + if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() + showingMessage = null + } + for (q in queued) { + val result = hostState.showSnackbar(ToastVisuals(q)) + if (result == SnackbarResult.ActionPerformed) q.onAction?.invoke() + } } else { - showingMessage = null val result = hostState.showSnackbar(ToastVisuals(event)) if (result == SnackbarResult.ActionPerformed) event.onAction?.invoke() } From 4e15c4ed22485f3f153ba5a85c6e951c996654fb Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:18:00 -0600 Subject: [PATCH 31/33] Test --- .../cloudstream3/ui/compose/settings/SettingsFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt index cccd28ec467..f08a7340b05 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt @@ -66,9 +66,9 @@ class SettingsFragment : Fragment() { val d = version.buildDate // clipboardHelper(txt(R.string.extension_version), "$v $h $d") com.lagradost.cloudstream4.compose.toast.ToastController.post("Test default") - com.lagradost.cloudstream4.compose.toast.ToastController.postSuccess("Test success") - com.lagradost.cloudstream4.compose.toast.ToastController.postError("Test error") - com.lagradost.cloudstream4.compose.toast.ToastController.postWarning("Test warning") + com.lagradost.cloudstream4.compose.toast.ToastController.post("Test success") + com.lagradost.cloudstream4.compose.toast.ToastController.post("Test error") + com.lagradost.cloudstream4.compose.toast.ToastController.post("Test warning") }, ) } From 630f95734a3ea0dc880ef4491ac5bfb135399dae Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:22:41 -0600 Subject: [PATCH 32/33] Fix double --- .../com/lagradost/cloudstream4/compose/toast/ToastHost.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt index b596aaeb4d6..a96a642aa1a 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/toast/ToastHost.kt @@ -39,6 +39,11 @@ internal fun ToastEffectHost(hostState: SnackbarHostState) { showingMessage = latest.message val result = hostState.showSnackbar(ToastVisuals(latest)) if (result == SnackbarResult.ActionPerformed) latest.onAction?.invoke() + var leftover = ToastController.drain() + while (leftover != null) { + if (leftover.queue) queued.add(leftover) + leftover = ToastController.drain() + } showingMessage = null } for (q in queued) { From bdead6d3cbb00c96ef826d99e10b8eecdebee24b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 24 May 2026 17:25:55 -0600 Subject: [PATCH 33/33] Test --- .../cloudstream3/ui/compose/settings/SettingsFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt index f08a7340b05..cccd28ec467 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/compose/settings/SettingsFragment.kt @@ -66,9 +66,9 @@ class SettingsFragment : Fragment() { val d = version.buildDate // clipboardHelper(txt(R.string.extension_version), "$v $h $d") com.lagradost.cloudstream4.compose.toast.ToastController.post("Test default") - com.lagradost.cloudstream4.compose.toast.ToastController.post("Test success") - com.lagradost.cloudstream4.compose.toast.ToastController.post("Test error") - com.lagradost.cloudstream4.compose.toast.ToastController.post("Test warning") + com.lagradost.cloudstream4.compose.toast.ToastController.postSuccess("Test success") + com.lagradost.cloudstream4.compose.toast.ToastController.postError("Test error") + com.lagradost.cloudstream4.compose.toast.ToastController.postWarning("Test warning") }, ) }