diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.android.kt new file mode 100644 index 000000000..23f99ee86 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.android.kt @@ -0,0 +1,17 @@ +package com.nuvio.app.features.settings + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import com.nuvio.app.R +import nuvio.composeapp.generated.resources.Res +import nuvio.composeapp.generated.resources.rating_tmdb +import org.jetbrains.compose.resources.painterResource as composePainterResource + +@Composable +internal actual fun integrationLogoPainter(logo: IntegrationLogo): Painter = + when (logo) { + IntegrationLogo.Tmdb -> composePainterResource(Res.drawable.rating_tmdb) + IntegrationLogo.Trakt -> painterResource(id = R.drawable.trakt_tv_favicon) + IntegrationLogo.MdbList -> painterResource(id = R.drawable.mdblist_logo) + } diff --git a/composeApp/src/androidMain/res/drawable/mdblist_logo.xml b/composeApp/src/androidMain/res/drawable/mdblist_logo.xml new file mode 100644 index 000000000..dcce61e15 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/mdblist_logo.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/mdblist_logo.svg b/composeApp/src/commonMain/composeResources/drawable/mdblist_logo.svg new file mode 100644 index 000000000..492e50f32 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/mdblist_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchScreen.kt index 60c78976a..ed1e69cd0 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/search/SearchScreen.kt @@ -68,6 +68,7 @@ fun SearchScreen( val recentSearches by SearchHistoryRepository.uiState.collectAsStateWithLifecycle() val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle() var query by rememberSaveable { mutableStateOf("") } + var lastRequestedQuery by rememberSaveable { mutableStateOf(null) } val listState = rememberLazyListState() val headerTitle by remember(query, listState) { derivedStateOf { @@ -109,9 +110,11 @@ fun SearchScreen( LaunchedEffect(query, addonRefreshKey) { val normalizedQuery = query.trim() if (normalizedQuery.isBlank()) { + lastRequestedQuery = null SearchRepository.clear() } else { delay(350) + lastRequestedQuery = normalizedQuery SearchRepository.search( query = normalizedQuery, addons = addonsUiState.addons, @@ -134,9 +137,11 @@ fun SearchScreen( } } - LaunchedEffect(query, uiState.sections) { + LaunchedEffect(query, lastRequestedQuery, uiState.isLoading, uiState.sections) { val normalizedQuery = query.trim() - if (normalizedQuery.isBlank() || uiState.sections.isEmpty()) return@LaunchedEffect + if (normalizedQuery.isBlank()) return@LaunchedEffect + if (lastRequestedQuery != normalizedQuery) return@LaunchedEffect + if (uiState.isLoading || uiState.sections.isEmpty()) return@LaunchedEffect SearchHistoryRepository.recordSearch(normalizedQuery) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt index dc839b7a6..5f58804fc 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt @@ -2,19 +2,13 @@ package com.nuvio.app.features.settings import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ConnectedTv import androidx.compose.material.icons.rounded.Extension -import androidx.compose.material.icons.rounded.MovieFilter -import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Tune internal fun LazyListScope.contentDiscoveryContent( isTablet: Boolean, onAddonsClick: () -> Unit, onHomescreenClick: () -> Unit, - onTmdbClick: () -> Unit, - onMdbListClick: () -> Unit, - onTraktClick: () -> Unit, ) { item { SettingsSection( @@ -32,38 +26,6 @@ internal fun LazyListScope.contentDiscoveryContent( } } } - item { - SettingsSection( - title = "ENRICHMENT", - isTablet = isTablet, - ) { - SettingsGroup(isTablet = isTablet) { - SettingsNavigationRow( - title = "TMDB Enrichment", - description = "Enhance detail pages with TMDB artwork, credits, episode metadata, and more.", - icon = Icons.Rounded.MovieFilter, - isTablet = isTablet, - onClick = onTmdbClick, - ) - SettingsGroupDivider(isTablet = isTablet) - SettingsNavigationRow( - title = "MDBList Ratings", - description = "Add IMDb, Rotten Tomatoes, Metacritic, and other external ratings to details pages.", - icon = Icons.Rounded.Star, - isTablet = isTablet, - onClick = onMdbListClick, - ) - SettingsGroupDivider(isTablet = isTablet) - SettingsNavigationRow( - title = "Trakt", - description = "Connect Trakt, sync watchlist lists, and save titles directly to Trakt.", - icon = Icons.Rounded.ConnectedTv, - isTablet = isTablet, - onClick = onTraktClick, - ) - } - } - } item { SettingsSection( title = "HOME", diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.kt new file mode 100644 index 000000000..4871bb163 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.kt @@ -0,0 +1,13 @@ +package com.nuvio.app.features.settings + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter + +internal enum class IntegrationLogo { + Tmdb, + Trakt, + MdbList, +} + +@Composable +internal expect fun integrationLogoPainter(logo: IntegrationLogo): Painter diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationsSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationsSettingsPage.kt new file mode 100644 index 000000000..f5bca62b1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/IntegrationsSettingsPage.kt @@ -0,0 +1,43 @@ +package com.nuvio.app.features.settings + +import androidx.compose.foundation.lazy.LazyListScope + +internal fun LazyListScope.integrationsContent( + isTablet: Boolean, + onTmdbClick: () -> Unit, + onMdbListClick: () -> Unit, + onTraktClick: () -> Unit, +) { + item { + SettingsSection( + title = "INTEGRATIONS", + isTablet = isTablet, + ) { + SettingsGroup(isTablet = isTablet) { + SettingsNavigationRow( + title = "TMDB Enrichment", + description = "Enhance detail pages with TMDB artwork, credits, episode metadata, and more.", + iconPainter = integrationLogoPainter(IntegrationLogo.Tmdb), + isTablet = isTablet, + onClick = onTmdbClick, + ) + SettingsGroupDivider(isTablet = isTablet) + SettingsNavigationRow( + title = "MDBList Ratings", + description = "Add IMDb, Rotten Tomatoes, Metacritic, and other external ratings to details pages.", + iconPainter = integrationLogoPainter(IntegrationLogo.MdbList), + isTablet = isTablet, + onClick = onMdbListClick, + ) + SettingsGroupDivider(isTablet = isTablet) + SettingsNavigationRow( + title = "Trakt", + description = "Connect Trakt, sync watchlist lists, and save titles directly to Trakt.", + iconPainter = integrationLogoPainter(IntegrationLogo.Trakt), + isTablet = isTablet, + onClick = onTraktClick, + ) + } + } + } +} 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 5b5c956b0..cb7f21c6e 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 @@ -35,7 +35,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -183,6 +185,7 @@ internal fun SettingsNavigationRow( title: String, description: String, icon: ImageVector? = null, + iconPainter: Painter? = null, isTablet: Boolean, onClick: () -> Unit, ) { @@ -205,7 +208,7 @@ internal fun SettingsNavigationRow( .widthIn(max = if (isTablet) 560.dp else 320.dp), verticalAlignment = Alignment.CenterVertically, ) { - if (icon != null) { + if (icon != null || iconPainter != null) { Surface( modifier = Modifier.size(iconSize), color = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f), @@ -216,11 +219,20 @@ internal fun SettingsNavigationRow( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - ) + if (iconPainter != null) { + androidx.compose.foundation.Image( + painter = iconPainter, + contentDescription = null, + modifier = Modifier.size(if (isTablet) 28.dp else 24.dp), + contentScale = ContentScale.Fit, + ) + } else if (icon != null) { + Icon( + imageVector = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + } } } Spacer(modifier = Modifier.width(if (isTablet) 16.dp else 14.dp)) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt index ce677c39c..4b931d528 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt @@ -18,6 +18,7 @@ internal enum class SettingsPage( Playback("Playback"), Appearance("Appearance"), ContentDiscovery("Content & Discovery"), + Integrations("Integrations"), TmdbEnrichment("TMDB Enrichment"), MdbListRatings("MDBList Ratings"), TraktAuthentication("Trakt"), @@ -29,7 +30,8 @@ internal fun SettingsPage.previousPage(): SettingsPage? = SettingsPage.Playback -> SettingsPage.Root SettingsPage.Appearance -> SettingsPage.Root SettingsPage.ContentDiscovery -> SettingsPage.Root - SettingsPage.TmdbEnrichment -> SettingsPage.ContentDiscovery - SettingsPage.MdbListRatings -> SettingsPage.ContentDiscovery - SettingsPage.TraktAuthentication -> SettingsPage.ContentDiscovery + SettingsPage.Integrations -> SettingsPage.Root + SettingsPage.TmdbEnrichment -> SettingsPage.Integrations + SettingsPage.MdbListRatings -> SettingsPage.Integrations + SettingsPage.TraktAuthentication -> SettingsPage.Integrations } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt index bce2c56fc..73acfc317 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.AccountCircle import androidx.compose.material.icons.rounded.Extension +import androidx.compose.material.icons.rounded.Link import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.People import androidx.compose.material.icons.rounded.PlayArrow @@ -13,6 +14,7 @@ internal fun LazyListScope.settingsRootContent( onPlaybackClick: () -> Unit, onAppearanceClick: () -> Unit, onContentDiscoveryClick: () -> Unit, + onIntegrationsClick: () -> Unit, onAccountClick: () -> Unit, onSwitchProfileClick: (() -> Unit)? = null, ) { @@ -71,6 +73,14 @@ internal fun LazyListScope.settingsRootContent( isTablet = isTablet, onClick = onContentDiscoveryClick, ) + SettingsGroupDivider(isTablet = isTablet) + SettingsNavigationRow( + title = "Integrations", + description = "Connect TMDB, Trakt, and MDBList services.", + icon = Icons.Rounded.Link, + isTablet = isTablet, + onClick = onIntegrationsClick, + ) } } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt index bfc104b36..666637b11 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt @@ -190,6 +190,7 @@ private fun MobileSettingsScreen( onPlaybackClick = { onPageChange(SettingsPage.Playback) }, onAppearanceClick = { onPageChange(SettingsPage.Appearance) }, onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) }, + onIntegrationsClick = { onPageChange(SettingsPage.Integrations) }, onAccountClick = onAccountClick, onSwitchProfileClick = onSwitchProfile, ) @@ -218,6 +219,9 @@ private fun MobileSettingsScreen( isTablet = false, onAddonsClick = onAddonsClick, onHomescreenClick = onHomescreenClick, + ) + SettingsPage.Integrations -> integrationsContent( + isTablet = false, onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) }, onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) }, onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) }, @@ -329,6 +333,7 @@ private fun TabletSettingsScreen( onPlaybackClick = { onPageChange(SettingsPage.Playback) }, onAppearanceClick = { onPageChange(SettingsPage.Appearance) }, onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) }, + onIntegrationsClick = { onPageChange(SettingsPage.Integrations) }, onAccountClick = onAccountClick, onSwitchProfileClick = onSwitchProfile, ) @@ -357,6 +362,9 @@ private fun TabletSettingsScreen( isTablet = true, onAddonsClick = onAddonsClick, onHomescreenClick = onHomescreenClick, + ) + SettingsPage.Integrations -> integrationsContent( + isTablet = true, onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) }, onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) }, onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) }, diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.ios.kt new file mode 100644 index 000000000..dfbe18a4d --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/IntegrationLogoPainter.ios.kt @@ -0,0 +1,17 @@ +package com.nuvio.app.features.settings + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import nuvio.composeapp.generated.resources.Res +import nuvio.composeapp.generated.resources.mdblist_logo +import nuvio.composeapp.generated.resources.rating_tmdb +import nuvio.composeapp.generated.resources.trakt_tv_favicon +import org.jetbrains.compose.resources.painterResource + +@Composable +internal actual fun integrationLogoPainter(logo: IntegrationLogo): Painter = + when (logo) { + IntegrationLogo.Tmdb -> painterResource(Res.drawable.rating_tmdb) + IntegrationLogo.Trakt -> painterResource(Res.drawable.trakt_tv_favicon) + IntegrationLogo.MdbList -> painterResource(Res.drawable.mdblist_logo) + }