mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 23:42:04 +00:00
Refactor LibraryScreen to use action targets
This commit is contained in:
parent
4dc054e51e
commit
c29f0cc064
1 changed files with 49 additions and 66 deletions
|
|
@ -21,10 +21,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.nuvio.app.core.network.NetworkCondition
|
import com.nuvio.app.core.network.NetworkCondition
|
||||||
import com.nuvio.app.core.network.NetworkStatusRepository
|
import com.nuvio.app.core.network.NetworkStatusRepository
|
||||||
|
import com.nuvio.app.core.ui.NuvioPosterActionSheet
|
||||||
import com.nuvio.app.core.ui.NuvioScreen
|
import com.nuvio.app.core.ui.NuvioScreen
|
||||||
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
||||||
import com.nuvio.app.core.ui.NuvioScreenHeader
|
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.NuvioToastController
|
||||||
import com.nuvio.app.core.ui.NuvioViewAllPillSize
|
import com.nuvio.app.core.ui.NuvioViewAllPillSize
|
||||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||||
|
|
@ -32,22 +32,20 @@ import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
||||||
import com.nuvio.app.features.home.components.HomePosterCard
|
import com.nuvio.app.features.home.components.HomePosterCard
|
||||||
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
||||||
import com.nuvio.app.features.profiles.ProfileRepository
|
import com.nuvio.app.features.profiles.ProfileRepository
|
||||||
|
import com.nuvio.app.features.watched.WatchedRepository
|
||||||
|
import com.nuvio.app.features.watching.application.WatchingActions
|
||||||
|
import com.nuvio.app.features.watching.application.WatchingState
|
||||||
import kotlinx.coroutines.launch
|
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(
|
private data class LibraryActionTarget(
|
||||||
val item: LibraryItem,
|
val item: LibraryItem,
|
||||||
val listKey: String? = null,
|
val section: LibrarySection,
|
||||||
val listTitle: String? = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LibraryScreen(
|
fun LibraryScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onPosterClick: ((LibraryItem) -> Unit)? = null,
|
onPosterClick: ((LibraryItem) -> Unit)? = null,
|
||||||
onPosterLongClick: ((LibraryItem) -> Unit)? = null,
|
|
||||||
onSectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
onSectionViewAllClick: ((LibrarySection) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val uiState by remember {
|
val uiState by remember {
|
||||||
|
|
@ -55,7 +53,8 @@ fun LibraryScreen(
|
||||||
LibraryRepository.uiState
|
LibraryRepository.uiState
|
||||||
}.collectAsStateWithLifecycle()
|
}.collectAsStateWithLifecycle()
|
||||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||||
var pendingRemovalTarget by remember { mutableStateOf<LibraryRemovalTarget?>(null) }
|
val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle()
|
||||||
|
var actionTarget by remember { mutableStateOf<LibraryActionTarget?>(null) }
|
||||||
var observedOfflineState by remember { mutableStateOf(false) }
|
var observedOfflineState by remember { mutableStateOf(false) }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val isTraktSource = uiState.sourceMode == LibrarySourceMode.TRAKT
|
val isTraktSource = uiState.sourceMode == LibrarySourceMode.TRAKT
|
||||||
|
|
@ -66,12 +65,11 @@ fun LibraryScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LaunchedEffect(networkStatusUiState.condition, isTraktSource) {
|
LaunchedEffect(networkStatusUiState.condition, isTraktSource) {
|
||||||
when (networkStatusUiState.condition) {
|
when (networkStatusUiState.condition) {
|
||||||
NetworkCondition.NoInternet,
|
NetworkCondition.NoInternet,
|
||||||
NetworkCondition.ServersUnreachable,
|
NetworkCondition.ServersUnreachable,
|
||||||
-> {
|
-> {
|
||||||
observedOfflineState = true
|
observedOfflineState = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +85,7 @@ fun LibraryScreen(
|
||||||
|
|
||||||
NetworkCondition.Unknown,
|
NetworkCondition.Unknown,
|
||||||
NetworkCondition.Checking,
|
NetworkCondition.Checking,
|
||||||
-> Unit
|
-> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,11 +100,7 @@ fun LibraryScreen(
|
||||||
.background(MaterialTheme.colorScheme.background),
|
.background(MaterialTheme.colorScheme.background),
|
||||||
) {
|
) {
|
||||||
NuvioScreenHeader(
|
NuvioScreenHeader(
|
||||||
title = if (isTraktSource) {
|
title = if (isTraktSource) "Trakt Library" else "Library",
|
||||||
stringResource(Res.string.library_trakt_title)
|
|
||||||
} else {
|
|
||||||
stringResource(Res.string.library_title)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
|
@ -131,13 +125,9 @@ fun LibraryScreen(
|
||||||
} else {
|
} else {
|
||||||
HomeEmptyStateCard(
|
HomeEmptyStateCard(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
title = if (isTraktSource) {
|
title = if (isTraktSource) "Couldn't load Trakt library" else "Couldn't load library",
|
||||||
stringResource(Res.string.library_trakt_load_failed)
|
|
||||||
} else {
|
|
||||||
stringResource(Res.string.library_load_failed)
|
|
||||||
},
|
|
||||||
message = uiState.errorMessage.orEmpty(),
|
message = uiState.errorMessage.orEmpty(),
|
||||||
actionLabel = stringResource(Res.string.action_retry),
|
actionLabel = "Retry",
|
||||||
onActionClick = retryLibraryLoad,
|
onActionClick = retryLibraryLoad,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -155,15 +145,11 @@ fun LibraryScreen(
|
||||||
} else {
|
} else {
|
||||||
HomeEmptyStateCard(
|
HomeEmptyStateCard(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
title = if (isTraktSource) {
|
title = if (isTraktSource) "Your Trakt library is empty" else "Your library is empty",
|
||||||
stringResource(Res.string.library_trakt_empty_title)
|
|
||||||
} else {
|
|
||||||
stringResource(Res.string.library_empty_title)
|
|
||||||
},
|
|
||||||
message = if (isTraktSource) {
|
message = if (isTraktSource) {
|
||||||
stringResource(Res.string.library_trakt_empty_message)
|
"Connect Trakt and save titles to your watchlist or personal lists."
|
||||||
} else {
|
} else {
|
||||||
stringResource(Res.string.library_empty_message)
|
"Saved titles will appear here after you tap Save on a details screen."
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -176,56 +162,53 @@ fun LibraryScreen(
|
||||||
onPosterClick = onPosterClick,
|
onPosterClick = onPosterClick,
|
||||||
onSectionViewAllClick = onSectionViewAllClick,
|
onSectionViewAllClick = onSectionViewAllClick,
|
||||||
onPosterLongClick = { item, section ->
|
onPosterLongClick = { item, section ->
|
||||||
pendingRemovalTarget = if (isTraktSource) {
|
actionTarget = LibraryActionTarget(item = item, section = section)
|
||||||
LibraryRemovalTarget(
|
|
||||||
item = item,
|
|
||||||
listKey = section.type,
|
|
||||||
listTitle = section.displayTitle,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
LibraryRemovalTarget(item = item)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NuvioStatusModal(
|
// Action sheet — same UI as home screen poster long-press
|
||||||
title = stringResource(Res.string.library_remove_title),
|
val target = actionTarget
|
||||||
message = pendingRemovalTarget?.let { target ->
|
if (target != null) {
|
||||||
val listTitle = target.listTitle
|
val preview = target.item.toMetaPreview()
|
||||||
if (listTitle.isNullOrBlank()) {
|
NuvioPosterActionSheet(
|
||||||
stringResource(Res.string.library_remove_message, target.item.name)
|
item = preview,
|
||||||
} else {
|
isSaved = LibraryRepository.isSaved(preview.id, preview.type),
|
||||||
stringResource(Res.string.library_remove_from_list_message, target.item.name, listTitle)
|
isWatched = WatchingState.isPosterWatched(
|
||||||
}
|
watchedKeys = watchedUiState.watchedKeys,
|
||||||
}.orEmpty(),
|
item = preview,
|
||||||
isVisible = pendingRemovalTarget != null,
|
),
|
||||||
confirmText = stringResource(Res.string.library_remove_confirm),
|
onDismiss = { actionTarget = null },
|
||||||
dismissText = stringResource(Res.string.action_cancel),
|
onToggleLibrary = {
|
||||||
onConfirm = {
|
actionTarget = null
|
||||||
val target = pendingRemovalTarget
|
val libraryItem = target.item
|
||||||
pendingRemovalTarget = null
|
if (isTraktSource) {
|
||||||
target?.let {
|
|
||||||
val listKey = target.listKey
|
|
||||||
if (listKey.isNullOrBlank()) {
|
|
||||||
LibraryRepository.remove(target.item.id)
|
|
||||||
} else {
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
LibraryRepository.removeFromList(target.item, listKey)
|
LibraryRepository.removeFromList(
|
||||||
|
libraryItem,
|
||||||
|
target.section.type,
|
||||||
|
)
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
NuvioToastController.show(
|
NuvioToastController.show(
|
||||||
error.message ?: getString(Res.string.trakt_lists_update_failed),
|
error.message ?: "Failed to update Trakt list",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LibraryRepository.toggleSaved(libraryItem)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
onToggleWatched = {
|
||||||
onDismiss = { pendingRemovalTarget = null },
|
actionTarget = null
|
||||||
)
|
coroutineScope.launch {
|
||||||
|
WatchingActions.togglePosterWatched(preview)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.librarySections(
|
private fun LazyListScope.librarySections(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue