mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-18 07:51:46 +00:00
feat: remaining places to use bottomsheetmodal
This commit is contained in:
parent
7a5c531a24
commit
f8eb52c299
3 changed files with 92 additions and 89 deletions
|
|
@ -126,6 +126,7 @@ import com.nuvio.app.features.library.LibrarySection
|
|||
import com.nuvio.app.features.library.LibrarySourceMode
|
||||
import com.nuvio.app.features.library.LibraryScreen
|
||||
import com.nuvio.app.features.library.toLibraryItem
|
||||
import com.nuvio.app.features.library.toMetaPreview
|
||||
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepository
|
||||
import com.nuvio.app.features.player.PlayerLaunch
|
||||
import com.nuvio.app.features.player.PlayerLaunchStore
|
||||
|
|
@ -276,6 +277,12 @@ data class CatalogRoute(
|
|||
val genre: String? = null,
|
||||
)
|
||||
|
||||
private data class PosterActionTarget(
|
||||
val preview: MetaPreview,
|
||||
val libraryItem: LibraryItem? = null,
|
||||
val libraryListKey: String? = null,
|
||||
)
|
||||
|
||||
enum class AppScreenTab {
|
||||
Home,
|
||||
Search,
|
||||
|
|
@ -556,7 +563,7 @@ private fun MainAppContent(
|
|||
}.collectAsStateWithLifecycle()
|
||||
val liquidGlassNativeTabBarSupported = remember { isLiquidGlassNativeTabBarSupported() }
|
||||
var showExitConfirmation by rememberSaveable { mutableStateOf(false) }
|
||||
var selectedPosterForActions by remember { mutableStateOf<MetaPreview?>(null) }
|
||||
var selectedPosterActionTarget by remember { mutableStateOf<PosterActionTarget?>(null) }
|
||||
var selectedContinueWatchingForActions by remember { mutableStateOf<ContinueWatchingItem?>(null) }
|
||||
var showLibraryListPicker by remember { mutableStateOf(false) }
|
||||
var pickerItem by remember { mutableStateOf<LibraryItem?>(null) }
|
||||
|
|
@ -1136,11 +1143,19 @@ private fun MainAppContent(
|
|||
},
|
||||
onPosterLongClick = { meta ->
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
selectedPosterForActions = meta
|
||||
selectedPosterActionTarget = PosterActionTarget(preview = meta)
|
||||
},
|
||||
onLibraryPosterClick = { item ->
|
||||
navController.navigate(DetailRoute(type = item.type, id = item.id))
|
||||
},
|
||||
onLibraryPosterLongClick = { item, section ->
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
selectedPosterActionTarget = PosterActionTarget(
|
||||
preview = item.toMetaPreview(),
|
||||
libraryItem = item,
|
||||
libraryListKey = section.type,
|
||||
)
|
||||
},
|
||||
onLibrarySectionViewAllClick = onLibrarySectionViewAllClick,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onContinueWatchingLongPress = onContinueWatchingLongPress,
|
||||
|
|
@ -1832,6 +1847,18 @@ private fun MainAppContent(
|
|||
onPosterClick = { meta ->
|
||||
navController.navigate(DetailRoute(type = meta.type, id = meta.id))
|
||||
},
|
||||
onPosterLongClick = { meta ->
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
selectedPosterActionTarget = if (route.manifestUrl == INTERNAL_LIBRARY_MANIFEST_URL) {
|
||||
PosterActionTarget(
|
||||
preview = meta,
|
||||
libraryItem = meta.toLibraryItem(savedAtEpochMs = 0L),
|
||||
libraryListKey = route.catalogId,
|
||||
)
|
||||
} else {
|
||||
PosterActionTarget(preview = meta)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
|
|
@ -1997,48 +2024,74 @@ private fun MainAppContent(
|
|||
}
|
||||
|
||||
NuvioPosterActionSheet(
|
||||
item = selectedPosterForActions,
|
||||
isSaved = selectedPosterForActions?.let { preview ->
|
||||
item = selectedPosterActionTarget?.preview,
|
||||
isSaved = selectedPosterActionTarget?.preview?.let { preview ->
|
||||
LibraryRepository.isSaved(preview.id, preview.type)
|
||||
} == true,
|
||||
isWatched = selectedPosterForActions?.let { preview ->
|
||||
isWatched = selectedPosterActionTarget?.preview?.let { preview ->
|
||||
WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
item = preview,
|
||||
)
|
||||
} == true,
|
||||
onDismiss = { selectedPosterForActions = null },
|
||||
onDismiss = { selectedPosterActionTarget = null },
|
||||
onToggleLibrary = {
|
||||
selectedPosterForActions?.let { preview ->
|
||||
val libraryItem = preview.toLibraryItem(savedAtEpochMs = 0L)
|
||||
if (!isTraktLibrarySource) {
|
||||
LibraryRepository.toggleSaved(libraryItem)
|
||||
} else {
|
||||
pickerItem = libraryItem
|
||||
pickerTitle = preview.name
|
||||
pickerTabs = LibraryRepository.libraryListTabs()
|
||||
pickerMembership = pickerTabs.associate { it.key to false }
|
||||
pickerPending = true
|
||||
pickerError = null
|
||||
showLibraryListPicker = true
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
val snapshot = LibraryRepository.getMembershipSnapshot(libraryItem)
|
||||
val tabs = LibraryRepository.libraryListTabs()
|
||||
pickerTabs = tabs
|
||||
pickerMembership = tabs.associate { tab ->
|
||||
tab.key to (snapshot[tab.key] == true)
|
||||
selectedPosterActionTarget?.let { target ->
|
||||
val preview = target.preview
|
||||
val libraryItem = target.libraryItem ?: preview.toLibraryItem(savedAtEpochMs = 0L)
|
||||
if (target.libraryItem != null) {
|
||||
if (isTraktLibrarySource) {
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
val listKey = target.libraryListKey
|
||||
if (listKey.isNullOrBlank()) {
|
||||
val currentMembership = LibraryRepository.getMembershipSnapshot(libraryItem)
|
||||
LibraryRepository.applyMembershipChanges(
|
||||
item = libraryItem,
|
||||
desiredMembership = currentMembership.mapValues { false },
|
||||
)
|
||||
} else {
|
||||
LibraryRepository.removeFromList(libraryItem, listKey)
|
||||
}
|
||||
}.onFailure { error ->
|
||||
NuvioToastController.show(
|
||||
error.message ?: getString(Res.string.trakt_lists_update_failed),
|
||||
)
|
||||
}
|
||||
}.onFailure { error ->
|
||||
pickerError = error.message ?: getString(Res.string.trakt_lists_load_failed)
|
||||
}
|
||||
pickerPending = false
|
||||
} else {
|
||||
LibraryRepository.remove(libraryItem.id)
|
||||
}
|
||||
} else {
|
||||
if (!isTraktLibrarySource) {
|
||||
LibraryRepository.toggleSaved(libraryItem)
|
||||
} else {
|
||||
pickerItem = libraryItem
|
||||
pickerTitle = preview.name
|
||||
pickerTabs = LibraryRepository.libraryListTabs()
|
||||
pickerMembership = pickerTabs.associate { it.key to false }
|
||||
pickerPending = true
|
||||
pickerError = null
|
||||
showLibraryListPicker = true
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
val snapshot = LibraryRepository.getMembershipSnapshot(libraryItem)
|
||||
val tabs = LibraryRepository.libraryListTabs()
|
||||
pickerTabs = tabs
|
||||
pickerMembership = tabs.associate { tab ->
|
||||
tab.key to (snapshot[tab.key] == true)
|
||||
}
|
||||
}.onFailure { error ->
|
||||
pickerError = error.message ?: getString(Res.string.trakt_lists_load_failed)
|
||||
}
|
||||
pickerPending = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onToggleWatched = {
|
||||
selectedPosterForActions?.let { preview ->
|
||||
selectedPosterActionTarget?.preview?.let { preview ->
|
||||
coroutineScope.launch {
|
||||
WatchingActions.togglePosterWatched(preview)
|
||||
}
|
||||
|
|
@ -2223,6 +2276,7 @@ private fun AppTabHost(
|
|||
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
||||
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
||||
onLibraryPosterClick: ((LibraryItem) -> Unit)? = null,
|
||||
onLibraryPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)? = null,
|
||||
onLibrarySectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
||||
onContinueWatchingClick: ((ContinueWatchingItem) -> Unit)? = null,
|
||||
onContinueWatchingLongPress: ((ContinueWatchingItem) -> Unit)? = null,
|
||||
|
|
@ -2276,6 +2330,7 @@ private fun AppTabHost(
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
scrollToTopRequests = libraryScrollToTopRequests,
|
||||
onPosterClick = onLibraryPosterClick,
|
||||
onPosterLongClick = onLibraryPosterLongClick,
|
||||
onSectionViewAllClick = onLibrarySectionViewAllClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ fun CatalogScreen(
|
|||
genre: String? = null,
|
||||
onBack: () -> Unit,
|
||||
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
||||
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val uiState by CatalogRepository.uiState.collectAsStateWithLifecycle()
|
||||
|
|
@ -187,6 +188,7 @@ fun CatalogScreen(
|
|||
cornerRadiusDp = posterCardStyle.cornerRadiusDp,
|
||||
hideLabels = posterCardStyle.hideLabelsEnabled,
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = onPosterLongClick?.let { { it(item) } },
|
||||
)
|
||||
}
|
||||
if (uiState.isLoading) {
|
||||
|
|
@ -257,6 +259,7 @@ private fun CatalogPosterTile(
|
|||
cornerRadiusDp: Int,
|
||||
hideLabels: Boolean,
|
||||
onClick: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
|
@ -267,7 +270,7 @@ private fun CatalogPosterTile(
|
|||
.aspectRatio(item.posterShape.catalogAspectRatio())
|
||||
.clip(RoundedCornerShape(cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.posterCardClickable(onClick = onClick, onLongClick = null),
|
||||
.posterCardClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
) {
|
||||
if (item.poster != null) {
|
||||
AsyncImage(
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ import com.nuvio.app.core.network.NetworkStatusRepository
|
|||
import com.nuvio.app.core.ui.NuvioScreen
|
||||
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
||||
import com.nuvio.app.core.ui.NuvioScreenHeader
|
||||
import com.nuvio.app.core.ui.NuvioStatusModal
|
||||
import com.nuvio.app.core.ui.NuvioToastController
|
||||
import com.nuvio.app.core.ui.NuvioViewAllPillSize
|
||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||
import com.nuvio.app.core.ui.nuvioBlockPointerPassthrough
|
||||
|
|
@ -38,20 +36,14 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
private data class LibraryRemovalTarget(
|
||||
val item: LibraryItem,
|
||||
val listKey: String? = null,
|
||||
val listTitle: String? = null,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun LibraryScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
scrollToTopRequests: Flow<Unit> = emptyFlow(),
|
||||
onPosterClick: ((LibraryItem) -> Unit)? = null,
|
||||
onPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)? = null,
|
||||
onSectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
||||
) {
|
||||
val uiState by remember {
|
||||
|
|
@ -59,7 +51,6 @@ fun LibraryScreen(
|
|||
LibraryRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||
var pendingRemovalTarget by remember { mutableStateOf<LibraryRemovalTarget?>(null) }
|
||||
var observedOfflineState by remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
|
@ -187,64 +178,18 @@ fun LibraryScreen(
|
|||
sections = uiState.sections,
|
||||
onPosterClick = onPosterClick,
|
||||
onSectionViewAllClick = onSectionViewAllClick,
|
||||
onPosterLongClick = { item, section ->
|
||||
pendingRemovalTarget = if (isTraktSource) {
|
||||
LibraryRemovalTarget(
|
||||
item = item,
|
||||
listKey = section.type,
|
||||
listTitle = section.displayTitle,
|
||||
)
|
||||
} else {
|
||||
LibraryRemovalTarget(item = item)
|
||||
}
|
||||
},
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NuvioStatusModal(
|
||||
title = stringResource(Res.string.library_remove_title),
|
||||
message = pendingRemovalTarget?.let { target ->
|
||||
val listTitle = target.listTitle
|
||||
if (listTitle.isNullOrBlank()) {
|
||||
stringResource(Res.string.library_remove_message, target.item.name)
|
||||
} else {
|
||||
stringResource(Res.string.library_remove_from_list_message, target.item.name, listTitle)
|
||||
}
|
||||
}.orEmpty(),
|
||||
isVisible = pendingRemovalTarget != null,
|
||||
confirmText = stringResource(Res.string.library_remove_confirm),
|
||||
dismissText = stringResource(Res.string.action_cancel),
|
||||
onConfirm = {
|
||||
val target = pendingRemovalTarget
|
||||
pendingRemovalTarget = null
|
||||
target?.let {
|
||||
val listKey = target.listKey
|
||||
if (listKey.isNullOrBlank()) {
|
||||
LibraryRepository.remove(target.item.id)
|
||||
} else {
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
LibraryRepository.removeFromList(target.item, listKey)
|
||||
}.onFailure { error ->
|
||||
NuvioToastController.show(
|
||||
error.message ?: getString(Res.string.trakt_lists_update_failed),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = { pendingRemovalTarget = null },
|
||||
)
|
||||
}
|
||||
|
||||
private fun LazyListScope.librarySections(
|
||||
sections: List<LibrarySection>,
|
||||
onPosterClick: ((LibraryItem) -> Unit)?,
|
||||
onSectionViewAllClick: ((LibrarySection) -> Unit)?,
|
||||
onPosterLongClick: (LibraryItem, LibrarySection) -> Unit,
|
||||
onPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)?,
|
||||
) {
|
||||
items(
|
||||
items = sections,
|
||||
|
|
@ -267,7 +212,7 @@ private fun LazyListScope.librarySections(
|
|||
HomePosterCard(
|
||||
item = item.toMetaPreview(),
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = { onPosterLongClick(item, section) },
|
||||
onLongClick = onPosterLongClick?.let { { it(item, section) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue