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)
+ }