From 50fa39c8941a94994f028df55f1b637ac5d9a919 Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:35:23 +0500 Subject: [PATCH 1/8] test(catalog): define channel control mutation contract --- .../ChannelPreferencesApiContractTest.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 core/database/src/test/kotlin/app/muxtv/database/ChannelPreferencesApiContractTest.kt diff --git a/core/database/src/test/kotlin/app/muxtv/database/ChannelPreferencesApiContractTest.kt b/core/database/src/test/kotlin/app/muxtv/database/ChannelPreferencesApiContractTest.kt new file mode 100644 index 000000000..bc1a897bb --- /dev/null +++ b/core/database/src/test/kotlin/app/muxtv/database/ChannelPreferencesApiContractTest.kt @@ -0,0 +1,22 @@ +package app.muxtv.database + +import app.muxtv.catalog.ChannelPreferencesRepository +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class ChannelPreferencesApiContractTest { + @Test + fun channelControlMutationsAreExposedByCatalogPort() { + val methodNames = ChannelPreferencesRepository::class.java.methods + .map { method -> method.name } + .toSet() + + assertThat(methodNames).containsAtLeast( + "setFavorite", + "setHidden", + "setCustomName", + "setChannelNumber", + "resetCustomization", + ) + } +} From f9f590b24d44cd47b33d7813fc9fd0c775c02a74 Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:36:49 +0500 Subject: [PATCH 2/8] feat(catalog): expose channel control mutations --- .../catalog/ChannelPreferencesRepository.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/catalog/api/src/main/kotlin/app/muxtv/catalog/ChannelPreferencesRepository.kt b/catalog/api/src/main/kotlin/app/muxtv/catalog/ChannelPreferencesRepository.kt index 1f5ecd14b..288f75cde 100644 --- a/catalog/api/src/main/kotlin/app/muxtv/catalog/ChannelPreferencesRepository.kt +++ b/catalog/api/src/main/kotlin/app/muxtv/catalog/ChannelPreferencesRepository.kt @@ -6,10 +6,40 @@ enum class ChannelFavoriteMutationResult { NotFound, } +enum class ChannelPreferenceMutationResult { + Applied, + Unchanged, + NotFound, + InvalidInput, +} + interface ChannelPreferencesRepository { suspend fun setFavorite( profileId: String, channelId: String, isFavorite: Boolean, ): ChannelFavoriteMutationResult + + suspend fun setHidden( + profileId: String, + channelId: String, + isHidden: Boolean, + ): ChannelPreferenceMutationResult + + suspend fun setCustomName( + profileId: String, + channelId: String, + customName: String?, + ): ChannelPreferenceMutationResult + + suspend fun setChannelNumber( + profileId: String, + channelId: String, + channelNumber: Int?, + ): ChannelPreferenceMutationResult + + suspend fun resetCustomization( + profileId: String, + channelId: String, + ): ChannelPreferenceMutationResult } From dde1c64f294646bef990d78314f1e41ed785ed5a Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:36:57 +0500 Subject: [PATCH 3/8] feat(catalog): scaffold channel control repository methods --- .../RoomChannelPreferencesRepository.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt b/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt index 95af20c2f..a039f79c5 100644 --- a/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt +++ b/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt @@ -1,6 +1,7 @@ package app.muxtv.database import app.muxtv.catalog.ChannelFavoriteMutationResult +import app.muxtv.catalog.ChannelPreferenceMutationResult import app.muxtv.catalog.ChannelPreferencesRepository internal class RoomChannelPreferencesRepository( @@ -15,4 +16,27 @@ internal class RoomChannelPreferencesRepository( FavoriteWriteResult.Unchanged -> ChannelFavoriteMutationResult.Unchanged FavoriteWriteResult.NotFound -> ChannelFavoriteMutationResult.NotFound } + + override suspend fun setHidden( + profileId: String, + channelId: String, + isHidden: Boolean, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + + override suspend fun setCustomName( + profileId: String, + channelId: String, + customName: String?, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + + override suspend fun setChannelNumber( + profileId: String, + channelId: String, + channelNumber: Int?, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + + override suspend fun resetCustomization( + profileId: String, + channelId: String, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput } From 2469bf9abb9659d48d04ff362bc51db759e3443e Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:37:37 +0500 Subject: [PATCH 4/8] test(catalog): specify channel control mutation behavior --- .../ChannelPreferencesRepositoryTest.kt | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt b/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt index 6fc30f5cb..20685d644 100644 --- a/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt +++ b/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt @@ -4,6 +4,7 @@ import androidx.room3.Room import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import app.muxtv.catalog.ChannelFavoriteMutationResult +import app.muxtv.catalog.ChannelPreferenceMutationResult import app.muxtv.catalog.ChannelQuery import app.muxtv.catalog.RejectAllPlaybackAccessPolicyResolver import app.muxtv.catalog.UnhandledPlaybackReferenceResolver @@ -98,11 +99,130 @@ class ChannelPreferencesRepositoryTest { assertThat(channel.summary.channelNumber).isEqualTo("7") } + @Test + fun hiddenMutationCreatesOverlayAndSupportsUnhide() = runTest { + assertThat( + channelPreferences.setHidden(PROFILE_ID, CHANNEL_ID, true), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + assertThat(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)).isNull() + + assertThat( + channelPreferences.setHidden(PROFILE_ID, CHANNEL_ID, true), + ).isEqualTo(ChannelPreferenceMutationResult.Unchanged) + + assertThat( + channelPreferences.setHidden(PROFILE_ID, CHANNEL_ID, false), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + assertThat(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)).isNotNull() + } + + @Test + fun customNameMutationTrimsAndPreservesOtherOverlayFields() = runTest { + database.catalogDao().insertOverlay( + UserChannelOverlayEntity( + profileId = PROFILE_ID, + canonicalChannelId = CHANNEL_ID, + isFavorite = true, + channelNumber = 7, + ), + ) + + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, " My News "), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + + val channel = requireNotNull(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)) + assertThat(channel.summary.displayName).isEqualTo("My News") + assertThat(channel.summary.channelNumber).isEqualTo("7") + assertThat(channel.summary.isFavorite).isTrue() + + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, "My News"), + ).isEqualTo(ChannelPreferenceMutationResult.Unchanged) + } + + @Test + fun customNameMutationRejectsInvalidInputWithoutCreatingOverlay() = runTest { + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, " "), + ).isEqualTo(ChannelPreferenceMutationResult.InvalidInput) + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, "News\u0000HD"), + ).isEqualTo(ChannelPreferenceMutationResult.InvalidInput) + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, "N".repeat(129)), + ).isEqualTo(ChannelPreferenceMutationResult.InvalidInput) + assertThat(database.catalogDao().countOverlays(PROFILE_ID)).isEqualTo(0) + } + + @Test + fun channelNumberMutationPersistsValidNumberAndSupportsReset() = runTest { + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, CHANNEL_ID, 7), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + assertThat( + requireNotNull(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)).summary.channelNumber, + ).isEqualTo("7") + + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, CHANNEL_ID, 0), + ).isEqualTo(ChannelPreferenceMutationResult.InvalidInput) + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, CHANNEL_ID, 10_000), + ).isEqualTo(ChannelPreferenceMutationResult.InvalidInput) + + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, CHANNEL_ID, null), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + assertThat( + requireNotNull(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)).summary.channelNumber, + ).isEqualTo("10") + } + + @Test + fun resetCustomizationRestoresProviderPresentationAndPreservesFavorite() = runTest { + database.catalogDao().insertOverlay( + UserChannelOverlayEntity( + profileId = PROFILE_ID, + canonicalChannelId = CHANNEL_ID, + isFavorite = true, + isHidden = true, + customName = "Hidden News", + channelNumber = 77, + ), + ) + + assertThat( + channelPreferences.resetCustomization(PROFILE_ID, CHANNEL_ID), + ).isEqualTo(ChannelPreferenceMutationResult.Applied) + + val channel = requireNotNull(playbackCatalog.getChannel(PROFILE_ID, CHANNEL_ID)) + assertThat(channel.summary.displayName).isEqualTo("News") + assertThat(channel.summary.channelNumber).isEqualTo("10") + assertThat(channel.summary.isFavorite).isTrue() + + assertThat( + channelPreferences.resetCustomization(PROFILE_ID, CHANNEL_ID), + ).isEqualTo(ChannelPreferenceMutationResult.Unchanged) + } + @Test fun missingChannelReturnsNotFoundWithoutCreatingOverlay() = runTest { assertThat( channelPreferences.setFavorite(PROFILE_ID, "missing-channel", true), ).isEqualTo(ChannelFavoriteMutationResult.NotFound) + assertThat( + channelPreferences.setHidden(PROFILE_ID, "missing-channel", true), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.setCustomName(PROFILE_ID, "missing-channel", "Missing"), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, "missing-channel", 1), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.resetCustomization(PROFILE_ID, "missing-channel"), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) assertThat(database.catalogDao().countOverlays(PROFILE_ID)).isEqualTo(0) } @@ -134,6 +254,18 @@ class ChannelPreferencesRepositoryTest { assertThat( channelPreferences.setFavorite(PROFILE_ID, CHANNEL_ID, true), ).isEqualTo(ChannelFavoriteMutationResult.NotFound) + assertThat( + channelPreferences.setHidden(PROFILE_ID, CHANNEL_ID, true), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.setCustomName(PROFILE_ID, CHANNEL_ID, "Old News"), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.setChannelNumber(PROFILE_ID, CHANNEL_ID, 7), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) + assertThat( + channelPreferences.resetCustomization(PROFILE_ID, CHANNEL_ID), + ).isEqualTo(ChannelPreferenceMutationResult.NotFound) assertThat(database.catalogDao().countOverlays(PROFILE_ID)).isEqualTo(0) } @@ -186,6 +318,7 @@ class ChannelPreferencesRepositoryTest { revisionStore.stageBatch( sourceId = SOURCE_ID, revisionNumber = 2, + startedAtEpochMillis = 3_000, entries = listOf( stagedEntry( providerChannelId = "provider-replacement", From e251df9bc2d92809fd7917353a44570ca8b54835 Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:38:20 +0500 Subject: [PATCH 5/8] test(catalog): keep channel control fixtures on active revision API --- .../app/muxtv/database/ChannelPreferencesRepositoryTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt b/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt index 20685d644..9565493c2 100644 --- a/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt +++ b/core/database/src/androidTest/kotlin/app/muxtv/database/ChannelPreferencesRepositoryTest.kt @@ -318,7 +318,6 @@ class ChannelPreferencesRepositoryTest { revisionStore.stageBatch( sourceId = SOURCE_ID, revisionNumber = 2, - startedAtEpochMillis = 3_000, entries = listOf( stagedEntry( providerChannelId = "provider-replacement", From c01e2f06325281783fd6118787299068949e5e7f Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:38:58 +0500 Subject: [PATCH 6/8] feat(catalog): persist channel control overlays --- .../muxtv/database/ChannelPreferencesDao.kt | 229 +++++++++++++++++- 1 file changed, 223 insertions(+), 6 deletions(-) diff --git a/core/database/src/main/kotlin/app/muxtv/database/ChannelPreferencesDao.kt b/core/database/src/main/kotlin/app/muxtv/database/ChannelPreferencesDao.kt index 90f4d0a14..de5d37228 100644 --- a/core/database/src/main/kotlin/app/muxtv/database/ChannelPreferencesDao.kt +++ b/core/database/src/main/kotlin/app/muxtv/database/ChannelPreferencesDao.kt @@ -6,8 +6,12 @@ import androidx.room3.OnConflictStrategy import androidx.room3.Query import androidx.room3.Transaction -internal data class ActiveChannelFavoriteRow( +internal data class ActiveChannelPreferenceRow( + val hasOverlay: Boolean, val isFavorite: Boolean, + val customName: String?, + val channelNumber: Int?, + val isHidden: Boolean, ) internal enum class FavoriteWriteResult { @@ -16,11 +20,23 @@ internal enum class FavoriteWriteResult { NotFound, } +internal enum class PreferenceWriteResult { + Applied, + Unchanged, + NotFound, + InvalidInput, +} + @Dao internal abstract class ChannelPreferencesDao { @Query( """ - SELECT COALESCE(user_channel_overlays.isFavorite, 0) AS isFavorite + SELECT + CASE WHEN user_channel_overlays.canonicalChannelId IS NULL THEN 0 ELSE 1 END AS hasOverlay, + COALESCE(user_channel_overlays.isFavorite, 0) AS isFavorite, + user_channel_overlays.customName AS customName, + user_channel_overlays.channelNumber AS channelNumber, + COALESCE(user_channel_overlays.isHidden, 0) AS isHidden FROM canonical_channels INNER JOIN stream_variants ON stream_variants.canonicalChannelId = canonical_channels.id @@ -33,14 +49,13 @@ internal abstract class ChannelPreferencesDao { AND user_channel_overlays.canonicalChannelId = canonical_channels.id WHERE canonical_channels.id = :channelId AND provider_channels.revisionNumber = sources.activeRevision - AND COALESCE(user_channel_overlays.isHidden, 0) = 0 LIMIT 1 """, ) - protected abstract suspend fun findActiveFavorite( + protected abstract suspend fun findActivePreferences( profileId: String, channelId: String, - ): ActiveChannelFavoriteRow? + ): ActiveChannelPreferenceRow? @Query( """ @@ -56,6 +71,79 @@ internal abstract class ChannelPreferencesDao { isFavorite: Boolean, ): Int + @Query( + """ + UPDATE user_channel_overlays + SET isHidden = :isHidden + WHERE profileId = :profileId + AND canonicalChannelId = :channelId + """, + ) + protected abstract suspend fun updateHidden( + profileId: String, + channelId: String, + isHidden: Boolean, + ): Int + + @Query( + """ + UPDATE user_channel_overlays + SET customName = :customName + WHERE profileId = :profileId + AND canonicalChannelId = :channelId + """, + ) + protected abstract suspend fun updateCustomName( + profileId: String, + channelId: String, + customName: String?, + ): Int + + @Query( + """ + UPDATE user_channel_overlays + SET channelNumber = :channelNumber + WHERE profileId = :profileId + AND canonicalChannelId = :channelId + """, + ) + protected abstract suspend fun updateChannelNumber( + profileId: String, + channelId: String, + channelNumber: Int?, + ): Int + + @Query( + """ + UPDATE user_channel_overlays + SET customName = NULL, + channelNumber = NULL, + isHidden = 0 + WHERE profileId = :profileId + AND canonicalChannelId = :channelId + """, + ) + protected abstract suspend fun resetPresentation( + profileId: String, + channelId: String, + ): Int + + @Query( + """ + DELETE FROM user_channel_overlays + WHERE profileId = :profileId + AND canonicalChannelId = :channelId + AND isFavorite = 0 + AND customName IS NULL + AND channelNumber IS NULL + AND isHidden = 0 + """, + ) + protected abstract suspend fun deleteDefaultOverlay( + profileId: String, + channelId: String, + ): Int + @Insert(onConflict = OnConflictStrategy.ABORT) protected abstract suspend fun insertOverlay(overlay: UserChannelOverlayEntity) @@ -68,8 +156,11 @@ internal abstract class ChannelPreferencesDao { require(profileId.isNotBlank()) require(channelId.isNotBlank()) - val current = findActiveFavorite(profileId, channelId) + val current = findActivePreferences(profileId, channelId) ?: return FavoriteWriteResult.NotFound + if (current.isHidden) { + return FavoriteWriteResult.NotFound + } if (current.isFavorite == isFavorite) { return FavoriteWriteResult.Unchanged } @@ -85,4 +176,130 @@ internal abstract class ChannelPreferencesDao { } return FavoriteWriteResult.Applied } + + @Transaction + open suspend fun setHidden( + profileId: String, + channelId: String, + isHidden: Boolean, + ): PreferenceWriteResult { + require(profileId.isNotBlank()) + require(channelId.isNotBlank()) + + val current = findActivePreferences(profileId, channelId) + ?: return PreferenceWriteResult.NotFound + if (current.isHidden == isHidden) { + return PreferenceWriteResult.Unchanged + } + + if (updateHidden(profileId, channelId, isHidden) == 0) { + insertOverlay( + UserChannelOverlayEntity( + profileId = profileId, + canonicalChannelId = channelId, + isHidden = isHidden, + ), + ) + } else if (!isHidden) { + deleteDefaultOverlay(profileId, channelId) + } + return PreferenceWriteResult.Applied + } + + @Transaction + open suspend fun setCustomName( + profileId: String, + channelId: String, + customName: String?, + ): PreferenceWriteResult { + require(profileId.isNotBlank()) + require(channelId.isNotBlank()) + + val normalizedName = normalizeCustomChannelName(customName) + ?: if (customName == null) null else return PreferenceWriteResult.InvalidInput + val current = findActivePreferences(profileId, channelId) + ?: return PreferenceWriteResult.NotFound + if (current.customName == normalizedName) { + return PreferenceWriteResult.Unchanged + } + + if (updateCustomName(profileId, channelId, normalizedName) == 0) { + insertOverlay( + UserChannelOverlayEntity( + profileId = profileId, + canonicalChannelId = channelId, + customName = normalizedName, + ), + ) + } else if (normalizedName == null) { + deleteDefaultOverlay(profileId, channelId) + } + return PreferenceWriteResult.Applied + } + + @Transaction + open suspend fun setChannelNumber( + profileId: String, + channelId: String, + channelNumber: Int?, + ): PreferenceWriteResult { + require(profileId.isNotBlank()) + require(channelId.isNotBlank()) + + if (channelNumber != null && channelNumber !in MIN_CUSTOM_CHANNEL_NUMBER..MAX_CUSTOM_CHANNEL_NUMBER) { + return PreferenceWriteResult.InvalidInput + } + val current = findActivePreferences(profileId, channelId) + ?: return PreferenceWriteResult.NotFound + if (current.channelNumber == channelNumber) { + return PreferenceWriteResult.Unchanged + } + + if (updateChannelNumber(profileId, channelId, channelNumber) == 0) { + insertOverlay( + UserChannelOverlayEntity( + profileId = profileId, + canonicalChannelId = channelId, + channelNumber = channelNumber, + ), + ) + } else if (channelNumber == null) { + deleteDefaultOverlay(profileId, channelId) + } + return PreferenceWriteResult.Applied + } + + @Transaction + open suspend fun resetCustomization( + profileId: String, + channelId: String, + ): PreferenceWriteResult { + require(profileId.isNotBlank()) + require(channelId.isNotBlank()) + + val current = findActivePreferences(profileId, channelId) + ?: return PreferenceWriteResult.NotFound + if (current.customName == null && current.channelNumber == null && !current.isHidden) { + return PreferenceWriteResult.Unchanged + } + + resetPresentation(profileId, channelId) + if (!current.isFavorite) { + deleteDefaultOverlay(profileId, channelId) + } + return PreferenceWriteResult.Applied + } } + +private fun normalizeCustomChannelName(value: String?): String? { + if (value == null) return null + val normalized = value.trim() + if (normalized.isEmpty()) return null + if (normalized.codePointCount(0, normalized.length) > MAX_CUSTOM_CHANNEL_NAME_CODE_POINTS) return null + if (normalized.any(Char::isISOControl)) return null + return normalized +} + +private const val MAX_CUSTOM_CHANNEL_NAME_CODE_POINTS = 128 +private const val MIN_CUSTOM_CHANNEL_NUMBER = 1 +private const val MAX_CUSTOM_CHANNEL_NUMBER = 9_999 From cc2dae9094a6cabc393554b186b1b718a904ef8a Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 08:39:09 +0500 Subject: [PATCH 7/8] feat(catalog): wire channel control mutations to room --- .../database/RoomChannelPreferencesRepository.kt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt b/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt index a039f79c5..2fdb93da7 100644 --- a/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt +++ b/core/database/src/main/kotlin/app/muxtv/database/RoomChannelPreferencesRepository.kt @@ -21,22 +21,29 @@ internal class RoomChannelPreferencesRepository( profileId: String, channelId: String, isHidden: Boolean, - ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + ): ChannelPreferenceMutationResult = dao.setHidden(profileId, channelId, isHidden).toApiResult() override suspend fun setCustomName( profileId: String, channelId: String, customName: String?, - ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + ): ChannelPreferenceMutationResult = dao.setCustomName(profileId, channelId, customName).toApiResult() override suspend fun setChannelNumber( profileId: String, channelId: String, channelNumber: Int?, - ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + ): ChannelPreferenceMutationResult = dao.setChannelNumber(profileId, channelId, channelNumber).toApiResult() override suspend fun resetCustomization( profileId: String, channelId: String, - ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.InvalidInput + ): ChannelPreferenceMutationResult = dao.resetCustomization(profileId, channelId).toApiResult() +} + +private fun PreferenceWriteResult.toApiResult(): ChannelPreferenceMutationResult = when (this) { + PreferenceWriteResult.Applied -> ChannelPreferenceMutationResult.Applied + PreferenceWriteResult.Unchanged -> ChannelPreferenceMutationResult.Unchanged + PreferenceWriteResult.NotFound -> ChannelPreferenceMutationResult.NotFound + PreferenceWriteResult.InvalidInput -> ChannelPreferenceMutationResult.InvalidInput } From 6dbd918a71efdaf8e714cb0caa61952875d5c8bc Mon Sep 17 00:00:00 2001 From: MrFr3di Date: Mon, 31 Aug 2026 09:25:41 +0500 Subject: [PATCH 8/8] test(tv): update channel preferences fake --- .../muxtv/TestChannelPreferencesRepository.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/tv/src/androidTest/kotlin/app/muxtv/TestChannelPreferencesRepository.kt b/app/tv/src/androidTest/kotlin/app/muxtv/TestChannelPreferencesRepository.kt index ea0c1e556..adaf4dbce 100644 --- a/app/tv/src/androidTest/kotlin/app/muxtv/TestChannelPreferencesRepository.kt +++ b/app/tv/src/androidTest/kotlin/app/muxtv/TestChannelPreferencesRepository.kt @@ -1,6 +1,7 @@ package app.muxtv import app.muxtv.catalog.ChannelFavoriteMutationResult +import app.muxtv.catalog.ChannelPreferenceMutationResult import app.muxtv.catalog.ChannelPreferencesRepository internal object NoChannelPreferencesRepository : ChannelPreferencesRepository { @@ -9,4 +10,27 @@ internal object NoChannelPreferencesRepository : ChannelPreferencesRepository { channelId: String, isFavorite: Boolean, ): ChannelFavoriteMutationResult = ChannelFavoriteMutationResult.NotFound + + override suspend fun setHidden( + profileId: String, + channelId: String, + isHidden: Boolean, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.NotFound + + override suspend fun setCustomName( + profileId: String, + channelId: String, + customName: String?, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.NotFound + + override suspend fun setChannelNumber( + profileId: String, + channelId: String, + channelNumber: Int?, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.NotFound + + override suspend fun resetCustomization( + profileId: String, + channelId: String, + ): ChannelPreferenceMutationResult = ChannelPreferenceMutationResult.NotFound }