fix: adjust toast ui

This commit is contained in:
tapframe 2026-04-07 19:49:39 +05:30
parent c235be2352
commit 7a76d9d38c
4 changed files with 124 additions and 41 deletions

View file

@ -76,6 +76,7 @@ import com.nuvio.app.core.ui.NuvioContinueWatchingActionSheet
import com.nuvio.app.core.ui.NuvioPosterActionSheet
import com.nuvio.app.core.ui.PlatformBackHandler
import com.nuvio.app.core.ui.configurePlatformImageLoader
import com.nuvio.app.core.ui.NuvioToastHost
import com.nuvio.app.core.ui.TraktListPickerDialog
import com.nuvio.app.core.ui.NuvioTheme
import com.nuvio.app.features.auth.AuthScreen
@ -1375,6 +1376,12 @@ private fun MainAppContent(
profileSwitchLoading = false
}
}
NuvioToastHost(
modifier = Modifier
.align(Alignment.TopCenter)
.zIndex(20f),
)
}
}

View file

@ -2,6 +2,7 @@ package com.nuvio.app.core.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
@ -46,6 +47,11 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -59,6 +65,9 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@Composable
fun NuvioScreen(
@ -436,42 +445,94 @@ fun NuvioStatusModal(
}
@Composable
fun NuvioPinnedCollectionToast(
visible: Boolean,
onDismiss: () -> Unit,
message: String = "Remove pin to top from collection to move",
fun NuvioToastHost(
modifier: Modifier = Modifier,
) {
LaunchedEffect(visible) {
if (visible) {
kotlinx.coroutines.delay(2500L)
onDismiss()
val toast by NuvioToastController.currentToast.collectAsState()
val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
val visibilityState = remember { MutableTransitionState(false) }
var renderedToast by remember { mutableStateOf<NuvioToastMessage?>(null) }
LaunchedEffect(toast?.id) {
val currentToast = toast
if (currentToast != null) {
renderedToast = currentToast
visibilityState.targetState = true
delay(currentToast.durationMillis)
NuvioToastController.dismiss(currentToast.id)
} else {
visibilityState.targetState = false
}
}
LaunchedEffect(
visibilityState.currentState,
visibilityState.targetState,
visibilityState.isIdle,
) {
if (visibilityState.isIdle && !visibilityState.currentState && !visibilityState.targetState) {
renderedToast = null
}
}
AnimatedVisibility(
visible = visible,
visibleState = visibilityState,
modifier = modifier,
enter = fadeIn() + slideInVertically { -it },
exit = fadeOut() + slideOutVertically { -it },
) {
val currentToast = renderedToast ?: return@AnimatedVisibility
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
contentAlignment = Alignment.Center,
.padding(top = statusBarTop + 12.dp)
.padding(horizontal = 16.dp),
contentAlignment = Alignment.TopCenter,
) {
Surface(
shape = RoundedCornerShape(12.dp),
color = MaterialTheme.colorScheme.inverseSurface,
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.colorScheme.surfaceContainerHigh,
tonalElevation = 6.dp,
shadowElevation = 4.dp,
shadowElevation = 10.dp,
) {
Text(
text = message,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
text = currentToast.message,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.inverseOnSurface,
color = MaterialTheme.colorScheme.onSurface,
)
}
}
}
}
data class NuvioToastMessage(
val id: Long,
val message: String,
val durationMillis: Long,
)
object NuvioToastController {
private val _currentToast = MutableStateFlow<NuvioToastMessage?>(null)
val currentToast = _currentToast.asStateFlow()
private var nextToastId = 0L
fun show(
message: String,
durationMillis: Long = 2500L,
) {
nextToastId += 1L
_currentToast.value = NuvioToastMessage(
id = nextToastId,
message = message,
durationMillis = durationMillis,
)
}
fun dismiss(id: Long? = null) {
val activeToast = _currentToast.value ?: return
if (id == null || activeToast.id == id) {
_currentToast.value = null
}
}
}

View file

@ -8,10 +8,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
@ -60,6 +62,7 @@ import com.nuvio.app.core.ui.NuvioScreen
import com.nuvio.app.core.ui.NuvioScreenHeader
import com.nuvio.app.core.ui.NuvioSectionLabel
import com.nuvio.app.core.ui.NuvioSurfaceCard
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
import com.nuvio.app.features.home.PosterShape
import sh.calvin.reorderable.ReorderableCollectionItemScope
import sh.calvin.reorderable.ReorderableItem
@ -72,6 +75,7 @@ fun CollectionEditorScreen(
onBack: () -> Unit,
) {
val state by CollectionEditorRepository.uiState.collectAsState()
val bottomInset = nuvioPlatformExtraBottomPadding
LaunchedEffect(collectionId) {
CollectionEditorRepository.initialize(collectionId)
@ -93,16 +97,18 @@ fun CollectionEditorScreen(
)
}
NuvioScreen {
stickyHeader {
NuvioScreenHeader(
title = if (state.isNew) "New Collection" else "Edit Collection",
onBack = onBack,
)
}
Box(modifier = Modifier.fillMaxSize()) {
NuvioScreen(
modifier = Modifier.fillMaxSize(),
) {
stickyHeader {
NuvioScreenHeader(
title = if (state.isNew) "New Collection" else "Edit Collection",
onBack = onBack,
)
}
// Title
item {
item {
NuvioInputField(
value = state.title,
onValueChange = { CollectionEditorRepository.setTitle(it) },
@ -110,8 +116,7 @@ fun CollectionEditorScreen(
)
}
// Backdrop URL
item {
item {
NuvioInputField(
value = state.backdropImageUrl,
onValueChange = { CollectionEditorRepository.setBackdropImageUrl(it) },
@ -119,8 +124,7 @@ fun CollectionEditorScreen(
)
}
// Pin to Top
item {
item {
NuvioSurfaceCard {
Row(
modifier = Modifier
@ -325,9 +329,25 @@ fun CollectionEditorScreen(
}
}
// Save button
item {
Spacer(modifier = Modifier.height(8.dp))
item {
Spacer(modifier = Modifier.height(96.dp + bottomInset))
}
}
Surface(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth(),
color = MaterialTheme.colorScheme.background.copy(alpha = 0.96f),
tonalElevation = 6.dp,
shadowElevation = 10.dp,
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
.padding(bottom = bottomInset),
) {
NuvioPrimaryButton(
text = if (state.isNew) "Create Collection" else "Save Changes",
enabled = state.title.isNotBlank(),
@ -337,6 +357,7 @@ fun CollectionEditorScreen(
}
},
)
}
}
}
}

View file

@ -32,7 +32,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.nuvio.app.core.ui.NuvioActionLabel
import com.nuvio.app.core.ui.NuvioPinnedCollectionToast
import com.nuvio.app.core.ui.NuvioToastController
import com.nuvio.app.features.home.HomeCatalogSettingsItem
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
import com.nuvio.app.features.home.components.HomeEmptyStateCard
@ -114,20 +114,14 @@ internal fun LazyListScope.homescreenSettingsContent(
)
},
) {
var showPinnedToast by remember { mutableStateOf(false) }
val hapticFeedback = LocalHapticFeedback.current
NuvioPinnedCollectionToast(
visible = showPinnedToast,
onDismiss = { showPinnedToast = false },
)
HomescreenCatalogList(
isTablet = isTablet,
items = items,
onPinnedDragAttempt = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
showPinnedToast = true
NuvioToastController.show("Remove pin to top from collection to move")
},
)
}