From 9f3ead522178c1e5a91e013db200184d25f16c29 Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:21:50 +0530 Subject: [PATCH] feat: collections reordering --- .../com/nuvio/app/core/ui/NuvioComponents.kt | 45 +++++++ .../home/HomeCatalogSettingsRepository.kt | 115 ++++++++++++++---- .../com/nuvio/app/features/home/HomeScreen.kt | 79 ++++++------ .../settings/HomescreenSettingsPage.kt | 40 +++++- .../features/settings/SettingsComponents.kt | 60 +++++---- .../settings/SettingsFullScreenPages.kt | 7 ++ 6 files changed, 256 insertions(+), 90 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioComponents.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioComponents.kt index ba671eb45..5fd24e83d 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioComponents.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioComponents.kt @@ -1,8 +1,11 @@ package com.nuvio.app.core.ui import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -42,6 +45,7 @@ import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -430,3 +434,44 @@ fun NuvioStatusModal( } } } + +@Composable +fun NuvioPinnedCollectionToast( + visible: Boolean, + onDismiss: () -> Unit, + message: String = "Remove pin to top from collection to move", +) { + LaunchedEffect(visible) { + if (visible) { + kotlinx.coroutines.delay(2500L) + onDismiss() + } + } + + AnimatedVisibility( + visible = visible, + enter = fadeIn() + slideInVertically { -it }, + exit = fadeOut() + slideOutVertically { -it }, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + contentAlignment = Alignment.Center, + ) { + Surface( + shape = RoundedCornerShape(12.dp), + color = MaterialTheme.colorScheme.inverseSurface, + tonalElevation = 6.dp, + shadowElevation = 4.dp, + ) { + Text( + text = message, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.inverseOnSurface, + ) + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt index c4f3214cc..9a7411fc1 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt @@ -1,6 +1,8 @@ package com.nuvio.app.features.home import com.nuvio.app.features.addons.ManagedAddon +import com.nuvio.app.features.collection.Collection +import com.nuvio.app.features.collection.CollectionRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,6 +19,9 @@ data class HomeCatalogSettingsItem( val enabled: Boolean = true, val heroSourceEnabled: Boolean = true, val order: Int = 0, + val isCollection: Boolean = false, + val collectionId: String? = null, + val isPinnedToTop: Boolean = false, ) { val displayTitle: String get() = customTitle.ifBlank { defaultTitle } @@ -78,6 +83,7 @@ object HomeCatalogSettingsRepository { private var hasLoaded = false private var definitions: List = emptyList() + private var collectionDefinitions: List = emptyList() private var preferences: MutableMap = mutableMapOf() private var heroEnabled = true @@ -86,12 +92,14 @@ object HomeCatalogSettingsRepository { preferences.clear() heroEnabled = true definitions = emptyList() + collectionDefinitions = emptyList() _uiState.value = HomeCatalogSettingsUiState() } fun clearLocalState() { hasLoaded = false definitions = emptyList() + collectionDefinitions = emptyList() preferences.clear() heroEnabled = true _uiState.value = HomeCatalogSettingsUiState() @@ -100,7 +108,8 @@ object HomeCatalogSettingsRepository { fun syncCatalogs(addons: List) { ensureLoaded() definitions = buildHomeCatalogDefinitions(addons) - if (definitions.isEmpty()) { + collectionDefinitions = buildCollectionDefinitions(CollectionRepository.collections.value) + if (definitions.isEmpty() && collectionDefinitions.isEmpty()) { publish() return } @@ -109,6 +118,14 @@ object HomeCatalogSettingsRepository { persist() } + fun syncCollections(collections: List) { + ensureLoaded() + collectionDefinitions = buildCollectionDefinitions(collections) + normalizePreferences() + publish() + persist() + } + internal fun snapshot(): HomeCatalogSettingsSnapshot { ensureLoaded() return HomeCatalogSettingsSnapshot( @@ -176,13 +193,11 @@ object HomeCatalogSettingsRepository { fun moveByIndex(fromIndex: Int, toIndex: Int) { ensureLoaded() - if (definitions.isEmpty()) return - val orderedKeys = definitions - .sortedBy { definition -> preferences[definition.key]?.order ?: Int.MAX_VALUE } - .map { it.key } - .toMutableList() - if (fromIndex !in orderedKeys.indices || toIndex !in orderedKeys.indices) return + val allKeys = allOrderedKeys() + if (allKeys.isEmpty()) return + if (fromIndex !in allKeys.indices || toIndex !in allKeys.indices) return if (fromIndex == toIndex) return + val orderedKeys = allKeys.toMutableList() orderedKeys.add(toIndex, orderedKeys.removeAt(fromIndex)) orderedKeys.forEachIndexed { index, itemKey -> val current = preferences[itemKey] ?: return@forEachIndexed @@ -219,14 +234,19 @@ object HomeCatalogSettingsRepository { private fun normalizePreferences() { val current = preferences - val orderedDefinitions = definitions.mapIndexed { defaultIndex, definition -> + data class UnifiedEntry(val key: String, val isCollection: Boolean) + val catalogEntries = definitions.map { UnifiedEntry(it.key, false) } + val collectionEntries = collectionDefinitions.map { UnifiedEntry(it.key, true) } + val allEntries = catalogEntries + collectionEntries + + val orderedEntries = allEntries.mapIndexed { defaultIndex, entry -> Triple( - definition, - current[definition.key]?.order ?: defaultIndex, + entry, + current[entry.key]?.order ?: (definitions.size + defaultIndex), defaultIndex, ) }.sortedWith( - compareBy>( + compareBy>( { it.second }, { it.third }, ), @@ -234,15 +254,19 @@ object HomeCatalogSettingsRepository { val normalized = mutableMapOf() var enabledHeroSourceCount = 0 - orderedDefinitions.forEachIndexed { index, definition -> - val stored = current[definition.key] - val heroSourceEnabled = (stored?.heroSourceEnabled ?: true) && - enabledHeroSourceCount < HERO_SOURCE_SELECTION_LIMIT + orderedEntries.forEachIndexed { index, entry -> + val stored = current[entry.key] + val heroSourceEnabled = if (entry.isCollection) { + false + } else { + (stored?.heroSourceEnabled ?: true) && + enabledHeroSourceCount < HERO_SOURCE_SELECTION_LIMIT + } if (heroSourceEnabled) { enabledHeroSourceCount += 1 } - normalized[definition.key] = StoredHomeCatalogPreference( - key = definition.key, + normalized[entry.key] = StoredHomeCatalogPreference( + key = entry.key, customTitle = stored?.customTitle.orEmpty(), enabled = stored?.enabled ?: true, heroSourceEnabled = heroSourceEnabled, @@ -253,8 +277,8 @@ object HomeCatalogSettingsRepository { } private fun publish() { - val items = definitions - .sortedBy { definition -> preferences[definition.key]?.order ?: Int.MAX_VALUE } + val collectionMap = collectionDefinitions.associateBy { it.key } + val catalogItems = definitions .map { definition -> val preference = preferences[definition.key] HomeCatalogSettingsItem( @@ -268,6 +292,25 @@ object HomeCatalogSettingsRepository { ) } + val collectionItems = collectionDefinitions.map { colDef -> + val preference = preferences[colDef.key] + HomeCatalogSettingsItem( + key = colDef.key, + defaultTitle = colDef.title, + addonName = colDef.subtitle, + customTitle = preference?.customTitle.orEmpty(), + enabled = preference?.enabled ?: true, + heroSourceEnabled = false, + order = preference?.order ?: 0, + isCollection = true, + collectionId = colDef.collectionId, + isPinnedToTop = colDef.isPinnedToTop, + ) + } + + val items = (catalogItems + collectionItems) + .sortedBy { it.order } + _uiState.value = HomeCatalogSettingsUiState( heroEnabled = heroEnabled, items = items, @@ -307,12 +350,8 @@ object HomeCatalogSettingsRepository { direction: Int, ) { ensureLoaded() - if (definitions.isEmpty()) return - - val orderedKeys = definitions - .sortedBy { definition -> preferences[definition.key]?.order ?: Int.MAX_VALUE } - .map { it.key } - .toMutableList() + val orderedKeys = allOrderedKeys().toMutableList() + if (orderedKeys.isEmpty()) return val currentIndex = orderedKeys.indexOf(key) if (currentIndex == -1) return @@ -332,4 +371,30 @@ object HomeCatalogSettingsRepository { persist() HomeRepository.applyCurrentSettings() } + + private fun allOrderedKeys(): List { + val catalogKeys = definitions.map { it.key } + val collectionKeys = collectionDefinitions.map { it.key } + return (catalogKeys + collectionKeys) + .sortedBy { key -> preferences[key]?.order ?: Int.MAX_VALUE } + } } + +internal data class CollectionCatalogDefinition( + val key: String, + val collectionId: String, + val title: String, + val subtitle: String, + val isPinnedToTop: Boolean, +) + +internal fun buildCollectionDefinitions(collections: List): List = + collections.filter { it.folders.isNotEmpty() }.map { collection -> + CollectionCatalogDefinition( + key = "collection_${collection.id}", + collectionId = collection.id, + title = collection.title, + subtitle = "${collection.folders.size} folder${if (collection.folders.size != 1) "s" else ""}", + isPinnedToTop = collection.pinToTop, + ) + } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt index 1bced8f9f..5380ce8fb 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeScreen.kt @@ -187,6 +187,10 @@ fun HomeScreen( HomeRepository.refresh(addonsUiState.addons) } + LaunchedEffect(collections) { + HomeCatalogSettingsRepository.syncCollections(collections) + } + LaunchedEffect(completedSeriesCandidates, metaProviderKey) { if (completedSeriesCandidates.isEmpty()) { nextUpItemsBySeries = emptyMap() @@ -368,48 +372,43 @@ fun HomeScreen( ) } } - // Pin-to-top collection rows - val pinnedCollections = collections.filter { it.pinToTop && it.folders.isNotEmpty() } - val unpinnedCollections = collections.filter { !it.pinToTop && it.folders.isNotEmpty() } - pinnedCollections.forEach { collection -> - item(key = "collection_${collection.id}") { - HomeCollectionRowSection( - collection = collection, - modifier = Modifier.padding(bottom = 12.dp), - onFolderClick = onFolderClick, - ) - } - } + val collectionsMap = collections.filter { it.folders.isNotEmpty() } + .associateBy { "collection_${it.id}" } + val sectionsMap = homeUiState.sections.associateBy { it.key } + val orderedItems = homeSettingsUiState.items.filter { it.enabled } - items( - count = homeUiState.sections.size, - key = { index -> homeUiState.sections[index].key }, - ) { index -> - val section = homeUiState.sections[index] - HomeCatalogRowSection( - section = section, - entries = section.items.take(HOME_CATALOG_PREVIEW_LIMIT), - modifier = Modifier.padding(bottom = 12.dp), - onViewAllClick = if (section.canOpenCatalog(HOME_CATALOG_PREVIEW_LIMIT)) { - onCatalogClick?.let { { it(section) } } - } else { - null - }, - watchedKeys = watchedUiState.watchedKeys, - onPosterClick = onPosterClick, - onPosterLongClick = onPosterLongClick, - ) - } - - // Unpinned collection rows after catalog sections - unpinnedCollections.forEach { collection -> - item(key = "collection_${collection.id}") { - HomeCollectionRowSection( - collection = collection, - modifier = Modifier.padding(bottom = 12.dp), - onFolderClick = onFolderClick, - ) + orderedItems.forEach { settingsItem -> + if (settingsItem.isCollection) { + val collection = collectionsMap[settingsItem.key] + if (collection != null) { + item(key = settingsItem.key) { + HomeCollectionRowSection( + collection = collection, + modifier = Modifier.padding(bottom = 12.dp), + onFolderClick = onFolderClick, + ) + } + } + } else { + val section = sectionsMap[settingsItem.key] + if (section != null && section.items.isNotEmpty()) { + item(key = settingsItem.key) { + HomeCatalogRowSection( + section = section, + entries = section.items.take(HOME_CATALOG_PREVIEW_LIMIT), + modifier = Modifier.padding(bottom = 12.dp), + onViewAllClick = if (section.canOpenCatalog(HOME_CATALOG_PREVIEW_LIMIT)) { + onCatalogClick?.let { { it(section) } } + } else { + null + }, + watchedKeys = watchedUiState.watchedKeys, + onPosterClick = onPosterClick, + onPosterLongClick = onPosterLongClick, + ) + } + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/HomescreenSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/HomescreenSettingsPage.kt index f3e67e50b..2f4b658f7 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/HomescreenSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/HomescreenSettingsPage.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.nuvio.app.core.ui.NuvioActionLabel +import com.nuvio.app.core.ui.NuvioPinnedCollectionToast import com.nuvio.app.features.home.HomeCatalogSettingsItem import com.nuvio.app.features.home.HomeCatalogSettingsRepository import com.nuvio.app.features.home.components.HomeEmptyStateCard @@ -71,7 +72,8 @@ internal fun LazyListScope.homescreenSettingsContent( } } item { - if (heroEnabled && items.isNotEmpty()) { + val catalogOnlyItems = items.filter { !it.isCollection } + if (heroEnabled && catalogOnlyItems.isNotEmpty()) { var heroSourcesExpanded by remember { mutableStateOf(false) } SettingsSection( title = "HERO SOURCES", @@ -79,7 +81,7 @@ internal fun LazyListScope.homescreenSettingsContent( ) { HeroSourcesDropdown( isTablet = isTablet, - items = items, + items = catalogOnlyItems, selectedHeroSourceCount = selectedHeroSourceCount, expanded = heroSourcesExpanded, onExpandedChange = { heroSourcesExpanded = it }, @@ -95,8 +97,15 @@ internal fun LazyListScope.homescreenSettingsContent( message = "Install an addon with board-compatible catalogs to configure Homescreen rows.", ) } else { + val catalogCount = items.count { !it.isCollection } + val collectionCount = items.count { it.isCollection } + val sectionTitle = when { + collectionCount > 0 && catalogCount > 0 -> "CATALOGS & COLLECTIONS" + collectionCount > 0 -> "COLLECTIONS" + else -> "CATALOGS" + } SettingsSection( - title = "CATALOGS", + title = sectionTitle, isTablet = isTablet, actions = { NuvioActionLabel( @@ -105,9 +114,21 @@ internal fun LazyListScope.homescreenSettingsContent( ) }, ) { + var showPinnedToast by remember { mutableStateOf(false) } + val hapticFeedback = LocalHapticFeedback.current + + NuvioPinnedCollectionToast( + visible = showPinnedToast, + onDismiss = { showPinnedToast = false }, + ) + HomescreenCatalogList( isTablet = isTablet, items = items, + onPinnedDragAttempt = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + showPinnedToast = true + }, ) } } @@ -219,6 +240,7 @@ private fun HomescreenSummaryCard( private fun HomescreenCatalogList( isTablet: Boolean, items: List, + onPinnedDragAttempt: () -> Unit, ) { var expandedKey by remember { mutableStateOf(null) } val hapticFeedback = LocalHapticFeedback.current @@ -226,6 +248,11 @@ private fun HomescreenCatalogList( val reorderableLazyListState = rememberReorderableLazyListState( lazyListState = lazyListState, ) { from, to -> + val fromItem = items.getOrNull(from.index) + val toItem = items.getOrNull(to.index) + if (fromItem?.isPinnedToTop == true || toItem?.isPinnedToTop == true) { + return@rememberReorderableLazyListState + } HomeCatalogSettingsRepository.moveByIndex(from.index, to.index) hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) } @@ -238,7 +265,11 @@ private fun HomescreenCatalogList( state = lazyListState, ) { itemsIndexed(items, key = { _, item -> item.key }) { index, item -> - ReorderableItem(reorderableLazyListState, key = item.key) { isDragging -> + ReorderableItem( + reorderableLazyListState, + key = item.key, + enabled = !item.isPinnedToTop, + ) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { @@ -256,6 +287,7 @@ private fun HomescreenCatalogList( onTitleChange = { HomeCatalogSettingsRepository.setCustomTitle(item.key, it) }, onEnabledChange = { HomeCatalogSettingsRepository.setEnabled(item.key, it) }, dragHandleScope = this@ReorderableItem, + onPinnedDragAttempt = onPinnedDragAttempt, ) } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt index b188fb266..5cdb8447f 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowForward +import androidx.compose.material.icons.rounded.Lock import androidx.compose.material.icons.rounded.Menu import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -342,6 +343,7 @@ internal fun HomescreenCatalogRow( onTitleChange: (String) -> Unit, onEnabledChange: (Boolean) -> Unit, dragHandleScope: ReorderableCollectionItemScope, + onPinnedDragAttempt: () -> Unit = {}, ) { val horizontalPadding = if (isTablet) 20.dp else 16.dp val verticalPadding = if (isTablet) 18.dp else 16.dp @@ -375,15 +377,19 @@ internal fun HomescreenCatalogRow( overflow = TextOverflow.Ellipsis, ) Text( - text = item.addonName, + text = if (item.isCollection) "Collection • ${item.addonName}" else item.addonName, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) Text( text = buildString { append(if (item.enabled) "Visible" else "Hidden") - append(" • ") - append(if (item.heroSourceEnabled) "Hero source" else "Not in hero") + if (item.isCollection) { + if (item.isPinnedToTop) append(" • Pinned to top") + } else { + append(" • ") + append(if (item.heroSourceEnabled) "Hero source" else "Not in hero") + } }, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, @@ -403,29 +409,41 @@ internal fun HomescreenCatalogRow( uncheckedTrackColor = MaterialTheme.colorScheme.outlineVariant, ), ) - IconButton( - modifier = with(dragHandleScope) { - Modifier.draggableHandle( - onDragStarted = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - }, - onDragStopped = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) - }, + if (item.isPinnedToTop) { + IconButton( + onClick = onPinnedDragAttempt, + ) { + Icon( + imageVector = Icons.Rounded.Lock, + contentDescription = "Pinned", + tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f), ) - }, - onClick = {}, - ) { - Icon( - imageVector = Icons.Rounded.Menu, - contentDescription = "Reorder", - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) + } + } else { + IconButton( + modifier = with(dragHandleScope) { + Modifier.draggableHandle( + onDragStarted = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + }, + onDragStopped = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) + }, + ) + }, + onClick = {}, + ) { + Icon( + imageVector = Icons.Rounded.Menu, + contentDescription = "Reorder", + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } - AnimatedVisibility(visible = expanded) { + AnimatedVisibility(visible = expanded && !item.isCollection) { Column( verticalArrangement = Arrangement.spacedBy(12.dp), ) { diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsFullScreenPages.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsFullScreenPages.kt index 9120b2613..543c7a7e6 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsFullScreenPages.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsFullScreenPages.kt @@ -14,6 +14,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nuvio.app.core.ui.NuvioScreen import com.nuvio.app.core.ui.NuvioScreenHeader import com.nuvio.app.features.addons.AddonRepository +import com.nuvio.app.features.collection.CollectionRepository import com.nuvio.app.features.details.MetaScreenSettingsRepository import com.nuvio.app.features.plugins.PluginRepository import com.nuvio.app.features.home.HomeCatalogSettingsRepository @@ -40,9 +41,11 @@ fun HomescreenSettingsScreen( } } val homescreenSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle() + val collections by CollectionRepository.collections.collectAsStateWithLifecycle() LaunchedEffect(Unit) { AddonRepository.initialize() + CollectionRepository.initialize() } LaunchedEffect(homescreenCatalogRefreshKey) { @@ -50,6 +53,10 @@ fun HomescreenSettingsScreen( HomeCatalogSettingsRepository.syncCatalogs(addonsUiState.addons) } + LaunchedEffect(collections) { + HomeCatalogSettingsRepository.syncCollections(collections) + } + NuvioScreen( modifier = Modifier .fillMaxSize()