feat: support for trakt library items removal from library screen

fixes #962
This commit is contained in:
tapframe 2026-05-09 01:18:21 +05:30
parent 951cb8f201
commit daedf05ea5
3 changed files with 64 additions and 12 deletions

View file

@ -1114,6 +1114,7 @@
<string name="downloads_live_failed">Download failed</string>
<string name="downloads_live_paused">Paused %1$s</string>
<string name="library_remove_confirm">Remove</string>
<string name="library_remove_from_list_message">Remove %1$s from %2$s?</string>
<string name="library_remove_message">Remove %1$s from your library?</string>
<string name="library_remove_title">Remove from Library?</string>
<string name="media_movie">Movie</string>

View file

@ -296,6 +296,14 @@ object LibraryRepository {
}
}
suspend fun removeFromList(item: LibraryItem, listKey: String) {
val desiredMembership = libraryMembershipWithRemovedList(
currentMembership = getMembershipSnapshot(item),
listKey = listKey,
)
applyMembershipChanges(item, desiredMembership)
}
private fun pushToServer() {
syncScope.launch {
runCatching {
@ -417,6 +425,14 @@ internal fun libraryMembershipWithLocal(
putAll(traktMembership)
}
internal fun libraryMembershipWithRemovedList(
currentMembership: Map<String, Boolean>,
listKey: String,
): Map<String, Boolean> =
currentMembership.toMutableMap().apply {
this[listKey] = false
}
private fun LibrarySyncItem.toLibraryItem(): LibraryItem = LibraryItem(
id = contentId,
type = contentType,

View file

@ -25,6 +25,7 @@ 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.features.home.components.HomeEmptyStateCard
@ -33,8 +34,15 @@ import com.nuvio.app.features.home.components.HomeSkeletonRow
import com.nuvio.app.features.profiles.ProfileRepository
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,
@ -46,7 +54,7 @@ fun LibraryScreen(
LibraryRepository.uiState
}.collectAsStateWithLifecycle()
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
var pendingRemovalItem by remember { mutableStateOf<LibraryItem?>(null) }
var pendingRemovalTarget by remember { mutableStateOf<LibraryRemovalTarget?>(null) }
var observedOfflineState by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
val isTraktSource = uiState.sourceMode == LibrarySourceMode.TRAKT
@ -165,9 +173,15 @@ fun LibraryScreen(
sections = uiState.sections,
onPosterClick = onPosterClick,
onSectionViewAllClick = onSectionViewAllClick,
onPosterLongClick = { item ->
if (!isTraktSource) {
pendingRemovalItem = item
onPosterLongClick = { item, section ->
pendingRemovalTarget = if (isTraktSource) {
LibraryRemovalTarget(
item = item,
listKey = section.type,
listTitle = section.displayTitle,
)
} else {
LibraryRemovalTarget(item = item)
}
},
)
@ -177,17 +191,38 @@ fun LibraryScreen(
NuvioStatusModal(
title = stringResource(Res.string.library_remove_title),
message = pendingRemovalItem?.let {
stringResource(Res.string.library_remove_message, it.name)
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 = pendingRemovalItem != null,
isVisible = pendingRemovalTarget != null,
confirmText = stringResource(Res.string.library_remove_confirm),
dismissText = stringResource(Res.string.action_cancel),
onConfirm = {
pendingRemovalItem?.id?.let(LibraryRepository::remove)
pendingRemovalItem = null
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 = { pendingRemovalItem = null },
onDismiss = { pendingRemovalTarget = null },
)
}
@ -195,7 +230,7 @@ private fun LazyListScope.librarySections(
sections: List<LibrarySection>,
onPosterClick: ((LibraryItem) -> Unit)?,
onSectionViewAllClick: ((LibrarySection) -> Unit)?,
onPosterLongClick: (LibraryItem) -> Unit,
onPosterLongClick: (LibraryItem, LibrarySection) -> Unit,
) {
items(
items = sections,
@ -218,7 +253,7 @@ private fun LazyListScope.librarySections(
HomePosterCard(
item = item.toMetaPreview(),
onClick = onPosterClick?.let { { it(item) } },
onLongClick = { onPosterLongClick(item) },
onLongClick = { onPosterLongClick(item, section) },
)
}
}