diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryScreen.kt index 8c94c676..85623512 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryScreen.kt @@ -21,10 +21,10 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nuvio.app.core.network.NetworkCondition 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.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 @@ -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.HomeSkeletonRow 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 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 listKey: String? = null, - val listTitle: String? = null, + val section: LibrarySection, ) @Composable fun LibraryScreen( modifier: Modifier = Modifier, onPosterClick: ((LibraryItem) -> Unit)? = null, - onPosterLongClick: ((LibraryItem) -> Unit)? = null, onSectionViewAllClick: ((LibrarySection) -> Unit)? = null, ) { val uiState by remember { @@ -55,7 +53,8 @@ fun LibraryScreen( LibraryRepository.uiState }.collectAsStateWithLifecycle() val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle() - var pendingRemovalTarget by remember { mutableStateOf(null) } + val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle() + var actionTarget by remember { mutableStateOf(null) } var observedOfflineState by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() val isTraktSource = uiState.sourceMode == LibrarySourceMode.TRAKT @@ -66,12 +65,11 @@ fun LibraryScreen( } } - LaunchedEffect(networkStatusUiState.condition, isTraktSource) { when (networkStatusUiState.condition) { NetworkCondition.NoInternet, NetworkCondition.ServersUnreachable, - -> { + -> { observedOfflineState = true } @@ -87,7 +85,7 @@ fun LibraryScreen( NetworkCondition.Unknown, NetworkCondition.Checking, - -> Unit + -> Unit } } @@ -102,11 +100,7 @@ fun LibraryScreen( .background(MaterialTheme.colorScheme.background), ) { NuvioScreenHeader( - title = if (isTraktSource) { - stringResource(Res.string.library_trakt_title) - } else { - stringResource(Res.string.library_title) - }, + title = if (isTraktSource) "Trakt Library" else "Library", modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.height(6.dp)) @@ -131,13 +125,9 @@ fun LibraryScreen( } else { HomeEmptyStateCard( modifier = Modifier.padding(horizontal = 16.dp), - title = if (isTraktSource) { - stringResource(Res.string.library_trakt_load_failed) - } else { - stringResource(Res.string.library_load_failed) - }, + title = if (isTraktSource) "Couldn't load Trakt library" else "Couldn't load library", message = uiState.errorMessage.orEmpty(), - actionLabel = stringResource(Res.string.action_retry), + actionLabel = "Retry", onActionClick = retryLibraryLoad, ) } @@ -155,15 +145,11 @@ fun LibraryScreen( } else { HomeEmptyStateCard( modifier = Modifier.padding(horizontal = 16.dp), - title = if (isTraktSource) { - stringResource(Res.string.library_trakt_empty_title) - } else { - stringResource(Res.string.library_empty_title) - }, + title = if (isTraktSource) "Your Trakt library is empty" else "Your library is empty", message = if (isTraktSource) { - stringResource(Res.string.library_trakt_empty_message) + "Connect Trakt and save titles to your watchlist or personal lists." } 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, onSectionViewAllClick = onSectionViewAllClick, onPosterLongClick = { item, section -> - pendingRemovalTarget = if (isTraktSource) { - LibraryRemovalTarget( - item = item, - listKey = section.type, - listTitle = section.displayTitle, - ) - } else { - LibraryRemovalTarget(item = item) - } + actionTarget = LibraryActionTarget(item = item, section = section) }, ) } } } - 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 { + // Action sheet — same UI as home screen poster long-press + val target = actionTarget + if (target != null) { + val preview = target.item.toMetaPreview() + NuvioPosterActionSheet( + item = preview, + isSaved = LibraryRepository.isSaved(preview.id, preview.type), + isWatched = WatchingState.isPosterWatched( + watchedKeys = watchedUiState.watchedKeys, + item = preview, + ), + onDismiss = { actionTarget = null }, + onToggleLibrary = { + actionTarget = null + val libraryItem = target.item + if (isTraktSource) { coroutineScope.launch { runCatching { - LibraryRepository.removeFromList(target.item, listKey) + LibraryRepository.removeFromList( + libraryItem, + target.section.type, + ) }.onFailure { error -> NuvioToastController.show( - error.message ?: getString(Res.string.trakt_lists_update_failed), + error.message ?: "Failed to update Trakt list", ) } } + } else { + LibraryRepository.toggleSaved(libraryItem) } - } - }, - onDismiss = { pendingRemovalTarget = null }, - ) + }, + onToggleWatched = { + actionTarget = null + coroutineScope.launch { + WatchingActions.togglePosterWatched(preview) + } + }, + ) + } } private fun LazyListScope.librarySections(