From 65817ad73b6660427615fdc02627e9839ccdd626 Mon Sep 17 00:00:00 2001
From: tapframe <85391825+tapframe@users.noreply.github.com>
Date: Wed, 22 Jul 2026 19:30:45 +0530
Subject: [PATCH] fix(simkl): separate completion from library status
---
.../composeResources/values/strings.xml | 1 +
.../commonMain/kotlin/com/nuvio/app/App.kt | 4 ++-
.../app/core/ui/TrackingListPickerDialog.kt | 6 ++--
.../app/features/details/MetaDetailsScreen.kt | 4 ++-
.../app/features/library/LibraryRepository.kt | 11 ++++--
.../library/TrackingMembershipFeedback.kt | 36 +++++++++++++++++++
.../app/features/simkl/SimklLibraryAdapter.kt | 26 ++++++++++----
.../features/simkl/SimklLibraryProjection.kt | 5 +++
.../features/simkl/SimklMutationRepository.kt | 26 ++++++++++++--
.../tracking/TrackingLibraryMembership.kt | 17 +++++++++
.../app/features/tracking/TrackingReads.kt | 7 +++-
.../app/features/tracking/TrackingWrites.kt | 8 ++++-
.../trakt/TraktTrackingLibraryProvider.kt | 4 ++-
.../simkl/SimklMutationRepositoryTest.kt | 3 +-
.../features/simkl/SimklProjectionsTest.kt | 3 ++
.../features/tracking/TrackingReadsTest.kt | 19 ++++++++++
16 files changed, 161 insertions(+), 19 deletions(-)
create mode 100644 composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/TrackingMembershipFeedback.kt
create mode 100644 composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingLibraryMembership.kt
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 1083f44c6..e6315c3c9 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -1446,6 +1446,7 @@
Failed to load Trakt lists
Failed to update Trakt lists
Failed to update tracking lists
+ %1$s placed this title in %2$s instead of %3$s
%1$s • %2$s
Update check failed
Download failed
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
index c6dbd361d..d403ff8bb 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
@@ -162,6 +162,7 @@ import com.nuvio.app.features.library.LibraryRepository
import com.nuvio.app.features.library.LibrarySection
import com.nuvio.app.features.library.LibrarySortOption
import com.nuvio.app.features.library.LibrarySourceMode
+import com.nuvio.app.features.library.showTrackingMembershipRewriteFeedback
import com.nuvio.app.features.library.LibraryScreen
import com.nuvio.app.features.library.toLibraryItem
import com.nuvio.app.features.library.toMetaPreview
@@ -3523,7 +3524,8 @@ private fun MainAppContent(
item = item,
desiredMembership = pickerMembership,
)
- }.onSuccess {
+ }.onSuccess { result ->
+ showTrackingMembershipRewriteFeedback(result)
showLibraryListPicker = false
pickerItem = null
pickerError = null
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/TrackingListPickerDialog.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/TrackingListPickerDialog.kt
index 135ee9516..f29694de1 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/TrackingListPickerDialog.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/TrackingListPickerDialog.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.nuvio.app.features.tracking.TrackingLibraryTab
+import com.nuvio.app.features.tracking.trackingMembershipDestinations
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.action_cancel
import nuvio.composeapp.generated.resources.action_save
@@ -48,6 +49,7 @@ fun TrackingListPickerDialog(
) {
if (!visible) return
val tokens = MaterialTheme.nuvio
+ val destinations = trackingMembershipDestinations(tabs)
BasicAlertDialog(
onDismissRequest = onDismiss,
@@ -80,7 +82,7 @@ fun TrackingListPickerDialog(
)
}
- if (isPending && tabs.isEmpty()) {
+ if (isPending && destinations.isEmpty()) {
Box(
modifier = Modifier
.fillMaxWidth()
@@ -108,7 +110,7 @@ fun TrackingListPickerDialog(
.height(NuvioTokens.Space.s80 + NuvioTokens.Space.s80 + NuvioTokens.Space.s80 + NuvioTokens.Space.s40),
verticalArrangement = Arrangement.spacedBy(tokens.spacing.controlGap),
) {
- items(items = tabs, key = { it.key }) { tab ->
+ items(items = destinations, key = { it.key }) { tab ->
val selected = membership[tab.key] == true
Row(
modifier = Modifier
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt
index 34d43c36c..314ecf8f4 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt
@@ -106,6 +106,7 @@ import com.nuvio.app.features.details.components.SeasonWatchedActionSheet
import com.nuvio.app.features.details.components.TrailerPlayerPopup
import com.nuvio.app.features.home.MetaPreview
import com.nuvio.app.features.library.LibraryRepository
+import com.nuvio.app.features.library.showTrackingMembershipRewriteFeedback
import com.nuvio.app.features.library.toLibraryItem
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.streams.StreamAutoPlayPolicy
@@ -1299,7 +1300,8 @@ fun MetaDetailsScreen(
item = meta.toLibraryItem(savedAtEpochMs = 0L),
desiredMembership = pickerMembership,
)
- }.onSuccess {
+ }.onSuccess { result ->
+ showTrackingMembershipRewriteFeedback(result)
showLibraryListPicker = false
}.onFailure { error ->
pickerError = error.message ?: getString(Res.string.tracking_lists_update_failed)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt
index 138149880..7cf306098 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt
@@ -12,6 +12,8 @@ import com.nuvio.app.features.profiles.ProfileRepository
import com.nuvio.app.features.tracking.TrackingLibraryProvider
import com.nuvio.app.features.tracking.TrackingLibraryTab
import com.nuvio.app.features.tracking.TrackingLibraryTabKind
+import com.nuvio.app.features.tracking.TrackingMembershipApplyResult
+import com.nuvio.app.features.tracking.TrackingMembershipResolution
import com.nuvio.app.features.tracking.TrackingProviderRegistry
import com.nuvio.app.features.tracking.TrackingRefreshIntent
import com.nuvio.app.features.tracking.TrackingSettingsRepository
@@ -387,7 +389,10 @@ object LibraryRepository {
return libraryMembershipWithLocal(inLocal = inLocal, providerMembership = memberships)
}
- suspend fun applyMembershipChanges(item: LibraryItem, desiredMembership: Map) {
+ suspend fun applyMembershipChanges(
+ item: LibraryItem,
+ desiredMembership: Map,
+ ): TrackingMembershipApplyResult {
ensureLoaded()
val localDesired = desiredMembership[LOCAL_LIBRARY_LIST_KEY] == true
val currentlyInLocal = localState.contains(item.id, item.type)
@@ -406,6 +411,7 @@ object LibraryRepository {
}
var firstFailure: Throwable? = null
+ val resolutions = mutableListOf()
TrackingProviderRegistry.connectedLibraryProviders().forEach { provider ->
val providerListKeys = provider.snapshot().tabs.mapTo(mutableSetOf(), TrackingLibraryTab::key)
val providerMembership = desiredMembership.filterKeys(providerListKeys::contains)
@@ -415,7 +421,7 @@ object LibraryRepository {
profileId = profileId,
item = item,
desiredMembership = providerMembership,
- )
+ )?.let(resolutions::add)
} catch (error: CancellationException) {
throw error
} catch (error: Throwable) {
@@ -426,6 +432,7 @@ object LibraryRepository {
}
publish()
firstFailure?.let { throw it }
+ return TrackingMembershipApplyResult(resolutions = resolutions)
}
suspend fun removeFromList(item: LibraryItem, listKey: String) {
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/TrackingMembershipFeedback.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/TrackingMembershipFeedback.kt
new file mode 100644
index 000000000..0d83618df
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/TrackingMembershipFeedback.kt
@@ -0,0 +1,36 @@
+package com.nuvio.app.features.library
+
+import com.nuvio.app.core.ui.NuvioToastController
+import com.nuvio.app.features.tracking.TrackingLibraryTab
+import com.nuvio.app.features.tracking.TrackingMembershipApplyResult
+import com.nuvio.app.features.tracking.TrackingProviderRegistry
+import nuvio.composeapp.generated.resources.Res
+import nuvio.composeapp.generated.resources.tracking_list_status_rewritten
+import org.jetbrains.compose.resources.getString
+
+internal suspend fun showTrackingMembershipRewriteFeedback(result: TrackingMembershipApplyResult) {
+ val rewrite = result.rewrites.firstOrNull() ?: return
+ val providerName = TrackingProviderRegistry.authProvider(rewrite.providerId)
+ ?.descriptor
+ ?.displayName
+ ?: rewrite.providerId.storageId.replaceFirstChar { char -> char.titlecase() }
+ val tabs = TrackingProviderRegistry.libraryProvider(rewrite.providerId)?.snapshot()?.tabs.orEmpty()
+ val requestedTitle = tabs.statusTitle(rewrite.requestedListKey, providerName)
+ val resolvedTitle = tabs.statusTitle(rewrite.resolvedListKey, providerName)
+ NuvioToastController.show(
+ getString(
+ Res.string.tracking_list_status_rewritten,
+ providerName,
+ resolvedTitle,
+ requestedTitle,
+ ),
+ )
+}
+
+private fun List.statusTitle(
+ key: String,
+ providerName: String,
+): String = firstOrNull { tab -> tab.key == key }
+ ?.title
+ ?.removePrefix("$providerName ")
+ ?: key.substringAfterLast(':')
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryAdapter.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryAdapter.kt
index 674a3baaf..28ace3bb9 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryAdapter.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryAdapter.kt
@@ -7,6 +7,7 @@ import com.nuvio.app.features.tracking.TrackingLibraryProvider
import com.nuvio.app.features.tracking.TrackingLibrarySnapshot
import com.nuvio.app.features.tracking.TrackingLibraryTab
import com.nuvio.app.features.tracking.TrackingLibraryTabKind
+import com.nuvio.app.features.tracking.TrackingMembershipResolution
import com.nuvio.app.features.tracking.TrackingProviderId
import com.nuvio.app.features.tracking.TrackingRefreshIntent
import kotlinx.coroutines.CoroutineScope
@@ -67,8 +68,8 @@ object SimklLibraryRepository {
profileId: Int,
item: LibraryItem,
desiredMembership: Map,
- ) {
- if (profileId != ProfileRepository.activeProfileId) return
+ ): TrackingMembershipResolution? {
+ if (profileId != ProfileRepository.activeProfileId) return null
ensureLoaded()
val desiredStatuses = simklLibraryStatusDefinitions.filter { definition ->
desiredMembership[definition.key] == true
@@ -80,7 +81,7 @@ object SimklLibraryRepository {
}) { "${desiredStatus?.title} does not support ${item.type}" }
val currentStatus = findItem(item.id, item.type)?.listKeys.orEmpty()
.firstNotNullOfOrNull(::simklLibraryStatusDefinition)
- if (desiredStatus == currentStatus) return
+ if (desiredStatus == currentStatus) return null
val snapshot = SimklSyncRepository.state.value.snapshot
val media = snapshot.mediaReference(
@@ -103,12 +104,25 @@ object SimklLibraryRepository {
SimklMutationRepository.removeFromList(profileId = profileId, items = listOf(media))
}
- else -> return
+ else -> return null
}
check(result.isComplete) {
"Simkl could not match ${result.notFoundCount} of ${result.attemptedCount} library items"
}
+ val resolution = desiredStatus?.let { requested ->
+ result.resolvedListStatuses
+ .singleOrNull()
+ ?.let(::simklLibraryStatusDefinition)
+ ?.let { resolved ->
+ TrackingMembershipResolution(
+ providerId = TrackingProviderId.SIMKL,
+ requestedListKey = requested.key,
+ resolvedListKey = resolved.key,
+ )
+ }
+ }
refresh(TrackingRefreshIntent.INVALIDATED)
+ return resolution
}
private fun publish(syncState: SimklSyncUiState) {
@@ -157,6 +171,7 @@ object SimklTrackingLibraryProvider : TrackingLibraryProvider {
},
selectionGroup = SIMKL_STATUS_SELECTION_GROUP,
supportedContentTypes = definition.supportedContentTypes,
+ isMembershipDestination = definition.isMembershipDestination,
)
},
hasLoaded = state.hasLoaded,
@@ -180,13 +195,12 @@ object SimklTrackingLibraryProvider : TrackingLibraryProvider {
profileId: Int,
item: LibraryItem,
desiredMembership: Map,
- ) {
+ ): TrackingMembershipResolution? =
SimklLibraryRepository.applyStatusMembership(
profileId = profileId,
item = item,
desiredMembership = desiredMembership,
)
- }
override suspend fun toggleDefaultMembership(profileId: Int, item: LibraryItem) {
val current = SimklLibraryRepository.statusMembership(item.id, item.type)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryProjection.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryProjection.kt
index 497b0f8dd..26b46a0c8 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryProjection.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklLibraryProjection.kt
@@ -13,6 +13,7 @@ internal data class SimklLibraryStatusDefinition(
val title: String,
val trackingStatus: TrackingListStatus,
val supportedContentTypes: Set,
+ val isMembershipDestination: Boolean = true,
)
internal data class SimklLibraryProjection(
@@ -48,6 +49,7 @@ internal val simklLibraryStatusDefinitions = listOf(
title = "Simkl Completed",
trackingStatus = TrackingListStatus.COMPLETED,
supportedContentTypes = setOf("movie", "series"),
+ isMembershipDestination = false,
),
SimklLibraryStatusDefinition(
status = SimklListStatus.DROPPED,
@@ -87,6 +89,9 @@ internal fun SimklSyncSnapshot.toSimklLibraryProjection(): SimklLibraryProjectio
internal fun simklLibraryStatusDefinition(key: String): SimklLibraryStatusDefinition? =
simklLibraryStatusDefinitions.firstOrNull { definition -> definition.key == key }
+internal fun simklLibraryStatusDefinition(status: TrackingListStatus): SimklLibraryStatusDefinition? =
+ simklLibraryStatusDefinitions.firstOrNull { definition -> definition.trackingStatus == status }
+
private fun SimklLibraryEntry.toLibraryItem(
listKey: String,
lastSyncedAtEpochMs: Long?,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklMutationRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklMutationRepository.kt
index ed73aaee7..5aebd8d17 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklMutationRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklMutationRepository.kt
@@ -24,6 +24,7 @@ import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlin.math.round
@@ -363,14 +364,33 @@ private fun TrackingExternalIds.toSimklJsonObjectOrNull(): JsonObject? {
}
private fun SimklApiResponse.toMutationResult(attemptedCount: Int, json: Json): TrackingMutationResult {
- val notFound = body
+ val payload = body
.takeIf(String::isNotBlank)
- ?.let { payload -> runCatching { json.parseToJsonElement(payload).jsonObject["not_found"]?.jsonObject }.getOrNull() }
+ ?.let { value -> runCatching { json.parseToJsonElement(value).jsonObject }.getOrNull() }
+ val notFound = payload
+ ?.get("not_found")
+ ?.let { value -> runCatching { value.jsonObject }.getOrNull() }
val notFoundCount = notFound
?.values
?.sumOf { value -> (value as? JsonArray)?.size ?: 0 }
?: 0
- return TrackingMutationResult(attemptedCount = attemptedCount, notFoundCount = notFoundCount)
+ val resolvedListStatuses = payload
+ ?.get("added")
+ ?.let { value -> runCatching { value.jsonObject }.getOrNull() }
+ ?.values
+ .orEmpty()
+ .flatMap { value -> (value as? JsonArray).orEmpty() }
+ .mapNotNull { value ->
+ val wireValue = runCatching {
+ value.jsonObject["to"]?.jsonPrimitive?.content
+ }.getOrNull()
+ TrackingListStatus.fromWireValue(wireValue)
+ }
+ return TrackingMutationResult(
+ attemptedCount = attemptedCount,
+ notFoundCount = notFoundCount,
+ resolvedListStatuses = resolvedListStatuses,
+ )
}
private fun Double.clampAndRoundProgress(): Double = round(coerceIn(0.0, 100.0) * 100.0) / 100.0
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingLibraryMembership.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingLibraryMembership.kt
new file mode 100644
index 000000000..9d72aec4c
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingLibraryMembership.kt
@@ -0,0 +1,17 @@
+package com.nuvio.app.features.tracking
+
+data class TrackingMembershipResolution(
+ val providerId: TrackingProviderId,
+ val requestedListKey: String,
+ val resolvedListKey: String,
+) {
+ val wasRewritten: Boolean
+ get() = requestedListKey != resolvedListKey
+}
+
+data class TrackingMembershipApplyResult(
+ val resolutions: List = emptyList(),
+) {
+ val rewrites: List
+ get() = resolutions.filter(TrackingMembershipResolution::wasRewritten)
+}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingReads.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingReads.kt
index 373109b35..b0d89b665 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingReads.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingReads.kt
@@ -20,6 +20,7 @@ data class TrackingLibraryTab(
val kind: TrackingLibraryTabKind,
val selectionGroup: String? = null,
val supportedContentTypes: Set? = null,
+ val isMembershipDestination: Boolean = true,
)
fun TrackingLibraryTab.supportsContentType(contentType: String): Boolean =
@@ -27,6 +28,10 @@ fun TrackingLibraryTab.supportsContentType(contentType: String): Boolean =
supported.equals(contentType, ignoreCase = true)
}
+internal fun trackingMembershipDestinations(
+ tabs: List,
+): List = tabs.filter(TrackingLibraryTab::isMembershipDestination)
+
fun toggleTrackingLibraryMembership(
tabs: List,
membership: Map,
@@ -88,7 +93,7 @@ interface TrackingLibraryProvider {
profileId: Int,
item: LibraryItem,
desiredMembership: Map,
- )
+ ): TrackingMembershipResolution?
suspend fun toggleDefaultMembership(profileId: Int, item: LibraryItem)
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingWrites.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingWrites.kt
index a3e246815..51a3298b1 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingWrites.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tracking/TrackingWrites.kt
@@ -5,7 +5,12 @@ enum class TrackingListStatus(val wireValue: String) {
PLAN_TO_WATCH("plantowatch"),
ON_HOLD("hold"),
COMPLETED("completed"),
- DROPPED("dropped"),
+ DROPPED("dropped");
+
+ companion object {
+ internal fun fromWireValue(value: String?): TrackingListStatus? =
+ entries.firstOrNull { status -> status.wireValue.equals(value, ignoreCase = true) }
+ }
}
enum class TrackingScrobbleAction(val wireValue: String) {
@@ -27,6 +32,7 @@ data class TrackingScrobbleEvent(
data class TrackingMutationResult(
val attemptedCount: Int,
val notFoundCount: Int = 0,
+ val resolvedListStatuses: List = emptyList(),
) {
val isComplete: Boolean
get() = notFoundCount == 0
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktTrackingLibraryProvider.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktTrackingLibraryProvider.kt
index a8fe68c8f..d8b2b1aad 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktTrackingLibraryProvider.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktTrackingLibraryProvider.kt
@@ -6,6 +6,7 @@ import com.nuvio.app.features.tracking.TrackingLibraryProvider
import com.nuvio.app.features.tracking.TrackingLibrarySnapshot
import com.nuvio.app.features.tracking.TrackingLibraryTab
import com.nuvio.app.features.tracking.TrackingLibraryTabKind
+import com.nuvio.app.features.tracking.TrackingMembershipResolution
import com.nuvio.app.features.tracking.TrackingProviderId
import com.nuvio.app.features.tracking.TrackingRefreshIntent
import kotlinx.coroutines.flow.Flow
@@ -69,11 +70,12 @@ object TraktTrackingLibraryProvider : TrackingLibraryProvider {
profileId: Int,
item: LibraryItem,
desiredMembership: Map,
- ) {
+ ): TrackingMembershipResolution? {
TraktLibraryRepository.applyMembershipChanges(
item = item,
changes = TraktMembershipChanges(desiredMembership = desiredMembership),
)
+ return null
}
override suspend fun toggleDefaultMembership(profileId: Int, item: LibraryItem) =
diff --git a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklMutationRepositoryTest.kt b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklMutationRepositoryTest.kt
index fcde57503..981b73c4c 100644
--- a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklMutationRepositoryTest.kt
+++ b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklMutationRepositoryTest.kt
@@ -105,7 +105,7 @@ class SimklMutationRepositoryTest {
val engine = RecordingEngine(
response(
status = 201,
- body = """{"added":{"movies":1},"not_found":{"movies":[{"title":"Missing"}],"shows":[]}}""",
+ body = """{"added":{"movies":[{"to":"completed"}]},"not_found":{"movies":[{"title":"Missing"}],"shows":[]}}""",
),
response(status = 409),
)
@@ -134,6 +134,7 @@ class SimklMutationRepositoryTest {
assertEquals(2, result.attemptedCount)
assertEquals(1, result.notFoundCount)
+ assertEquals(listOf(TrackingListStatus.COMPLETED), result.resolvedListStatuses)
assertFalse(result.isComplete)
assertEquals(listOf("/sync/add-to-list", "/scrobble/stop"), engine.paths)
assertEquals(2, committed)
diff --git a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklProjectionsTest.kt b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklProjectionsTest.kt
index 6440c724f..8f66b3ee4 100644
--- a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklProjectionsTest.kt
+++ b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklProjectionsTest.kt
@@ -71,6 +71,9 @@ class SimklProjectionsTest {
setOf(watchingDefinition.key),
projection.items.single { candidate -> candidate.id == "tt1520211" }.listKeys,
)
+ assertTrue(watchingDefinition.isMembershipDestination)
+ assertTrue(planDefinition.isMembershipDestination)
+ assertFalse(completedDefinition.isMembershipDestination)
}
@Test
diff --git a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/tracking/TrackingReadsTest.kt b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/tracking/TrackingReadsTest.kt
index 030ce7d5b..ae4e17ff1 100644
--- a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/tracking/TrackingReadsTest.kt
+++ b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/tracking/TrackingReadsTest.kt
@@ -53,6 +53,25 @@ class TrackingReadsTest {
assertTrue(tab("trakt:watchlist").supportsContentType("movie"))
}
+ @Test
+ fun `non destination status stays in selection group without appearing in picker`() {
+ val watching = tab("simkl:watching", selectionGroup = "simkl:status")
+ val completed = tab("simkl:completed", selectionGroup = "simkl:status")
+ .copy(isMembershipDestination = false)
+ val tabs = listOf(watching, completed)
+
+ assertEquals(listOf(watching), trackingMembershipDestinations(tabs))
+
+ val updated = toggleTrackingLibraryMembership(
+ tabs = tabs,
+ membership = mapOf(watching.key to false, completed.key to true),
+ key = watching.key,
+ )
+
+ assertEquals(true, updated[watching.key])
+ assertEquals(false, updated[completed.key])
+ }
+
private fun tab(
key: String,
providerId: TrackingProviderId? = TrackingProviderId.SIMKL,