mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-26 22:42:17 +00:00
fix(simkl): separate completion from library status
This commit is contained in:
parent
3064c7aa25
commit
65817ad73b
16 changed files with 161 additions and 19 deletions
|
|
@ -1446,6 +1446,7 @@
|
|||
<string name="trakt_lists_load_failed">Failed to load Trakt lists</string>
|
||||
<string name="trakt_lists_update_failed">Failed to update Trakt lists</string>
|
||||
<string name="tracking_lists_update_failed">Failed to update tracking lists</string>
|
||||
<string name="tracking_list_status_rewritten">%1$s placed this title in %2$s instead of %3$s</string>
|
||||
<string name="updates_asset_line">%1$s • %2$s</string>
|
||||
<string name="updates_check_failed">Update check failed</string>
|
||||
<string name="updates_download_failed">Download failed</string>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<String, Boolean>) {
|
||||
suspend fun applyMembershipChanges(
|
||||
item: LibraryItem,
|
||||
desiredMembership: Map<String, Boolean>,
|
||||
): 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<TrackingMembershipResolution>()
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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<TrackingLibraryTab>.statusTitle(
|
||||
key: String,
|
||||
providerName: String,
|
||||
): String = firstOrNull { tab -> tab.key == key }
|
||||
?.title
|
||||
?.removePrefix("$providerName ")
|
||||
?: key.substringAfterLast(':')
|
||||
|
|
@ -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<String, Boolean>,
|
||||
) {
|
||||
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<String, Boolean>,
|
||||
) {
|
||||
): 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)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ internal data class SimklLibraryStatusDefinition(
|
|||
val title: String,
|
||||
val trackingStatus: TrackingListStatus,
|
||||
val supportedContentTypes: Set<String>,
|
||||
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?,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<TrackingMembershipResolution> = emptyList(),
|
||||
) {
|
||||
val rewrites: List<TrackingMembershipResolution>
|
||||
get() = resolutions.filter(TrackingMembershipResolution::wasRewritten)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ data class TrackingLibraryTab(
|
|||
val kind: TrackingLibraryTabKind,
|
||||
val selectionGroup: String? = null,
|
||||
val supportedContentTypes: Set<String>? = 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<TrackingLibraryTab>,
|
||||
): List<TrackingLibraryTab> = tabs.filter(TrackingLibraryTab::isMembershipDestination)
|
||||
|
||||
fun toggleTrackingLibraryMembership(
|
||||
tabs: List<TrackingLibraryTab>,
|
||||
membership: Map<String, Boolean>,
|
||||
|
|
@ -88,7 +93,7 @@ interface TrackingLibraryProvider {
|
|||
profileId: Int,
|
||||
item: LibraryItem,
|
||||
desiredMembership: Map<String, Boolean>,
|
||||
)
|
||||
): TrackingMembershipResolution?
|
||||
suspend fun toggleDefaultMembership(profileId: Int, item: LibraryItem)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TrackingListStatus> = emptyList(),
|
||||
) {
|
||||
val isComplete: Boolean
|
||||
get() = notFoundCount == 0
|
||||
|
|
|
|||
|
|
@ -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<String, Boolean>,
|
||||
) {
|
||||
): TrackingMembershipResolution? {
|
||||
TraktLibraryRepository.applyMembershipChanges(
|
||||
item = item,
|
||||
changes = TraktMembershipChanges(desiredMembership = desiredMembership),
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun toggleDefaultMembership(profileId: Int, item: LibraryItem) =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue