Add real brand logos to Settings account rows, fix Catalogs icon, drop MAL

Reuses the app's existing ic_stremio/ic_nuvio/ic_trakt/ic_simkl/ic_anilist
drawables as icon slots on the shared Settings account rows, matching the
pattern already used for the Auth screen's provider buttons. Swaps the
Catalogs nav icon from a single open book to a stacked-books icon.

Also removes MyAnimeList support from the shared Settings feature end to
end (contracts, store, screen, Android data source, host wiring) since the
product no longer supports it — only Stremio/Nuvio/Trakt/Simkl/AniList
remain.
This commit is contained in:
KhooLy 2026-07-14 01:42:37 +03:00
parent 339e525d34
commit e45b3acaa4
8 changed files with 89 additions and 39 deletions

View file

@ -124,11 +124,9 @@ class AndroidSettingsDataSource(
avatarUrl = profile.avatarUrl,
isGuest = profile.isGuest,
hasTrakt = !profile.traktAccessToken.isNullOrBlank(),
hasMal = !profile.malAccessToken.isNullOrBlank(),
hasSimkl = !profile.simklAccessToken.isNullOrBlank(),
hasAnilist = !profile.anilistAccessToken.isNullOrBlank(),
hasAnySync = !profile.traktAccessToken.isNullOrBlank() ||
!profile.malAccessToken.isNullOrBlank() ||
!profile.simklAccessToken.isNullOrBlank() ||
!profile.anilistAccessToken.isNullOrBlank(),
tmdbApiKey = profile.tmdbApiKey,
@ -442,8 +440,6 @@ class AndroidSettingsDataSource(
it.copy(
traktAccessToken = null,
traktRefreshToken = null,
malAccessToken = null,
malRefreshToken = null,
simklAccessToken = null,
anilistAccessToken = null,
anilistRefreshToken = null,

View file

@ -201,6 +201,27 @@ internal fun AppRoutesHost(
modifier = Modifier.size(22.dp)
)
},
traktIcon = {
androidx.compose.foundation.Image(
painter = androidx.compose.ui.res.painterResource(id = com.fluxa.app.R.drawable.ic_trakt),
contentDescription = null,
modifier = Modifier.size(22.dp)
)
},
simklIcon = {
androidx.compose.foundation.Image(
painter = androidx.compose.ui.res.painterResource(id = com.fluxa.app.R.drawable.ic_simkl),
contentDescription = null,
modifier = Modifier.size(22.dp)
)
},
anilistIcon = {
androidx.compose.foundation.Image(
painter = androidx.compose.ui.res.painterResource(id = com.fluxa.app.R.drawable.ic_anilist),
contentDescription = null,
modifier = Modifier.size(22.dp)
)
},
onSettingsBackRequested = navigateBackSafely,
onManageAddonsRequested = { navigator.navigateTo(Screen.AddonStore) },
onConnectStremioRequested = {
@ -244,13 +265,6 @@ internal fun AppRoutesHost(
connectTrakt(context, activeProfile, profileManager, homeViewModel, onActiveProfileChanged, onTraktDeviceAuthChanged)
}
},
onConnectMalRequested = {
if (!activeProfile?.malAccessToken.isNullOrBlank()) {
onShowMalSheet()
} else {
connectMal(context, activeProfile, oauthPrefs, onPendingMalCodeVerifierChanged)
}
},
onConnectSimklRequested = {
if (!activeProfile?.simklAccessToken.isNullOrBlank()) {
onShowSimklSheet()

View file

@ -131,6 +131,7 @@ fun FluxaApp(
onSettingsAction: (SettingsAction) -> Unit = {},
onSwitchProfilesRequested: () -> Unit = {},
onSettingsBackRequested: () -> Unit = {},
settingsBrandIcons: com.fluxa.app.shared.feature.settings.SettingsBrandIcons = com.fluxa.app.shared.feature.settings.SettingsBrandIcons(),
addonStoreState: AddonStoreUiState? = null,
onAddonStoreAction: (AddonStoreAction) -> Unit = {},
onOpenUrlRequested: (String) -> Unit = {},
@ -225,6 +226,7 @@ fun FluxaApp(
onAction = onSettingsAction,
onSwitchProfilesRequested = onSwitchProfilesRequested,
onBackRequested = onSettingsBackRequested,
brandIcons = settingsBrandIcons,
modifier = Modifier.weight(1f)
)
state.destination == FluxaDestination.AddonStore && addonStoreState != null -> AddonStoreScreen(

View file

@ -71,6 +71,9 @@ fun FluxaAppHost(
authStartOnNuvio: Boolean = false,
nuvioIcon: @Composable () -> Unit = {},
stremioIcon: @Composable () -> Unit = {},
traktIcon: @Composable () -> Unit = {},
simklIcon: @Composable () -> Unit = {},
anilistIcon: @Composable () -> Unit = {},
biometricAvailable: Boolean = false,
onPickAvatarRequested: (onPicked: (String?) -> Unit) -> Unit = {},
onBiometricAuthRequested: (ProfileUiModel, onResult: (Boolean) -> Unit) -> Unit = { _, _ -> },
@ -78,7 +81,6 @@ fun FluxaAppHost(
onConnectStremioRequested: () -> Unit = {},
onConnectNuvioRequested: () -> Unit = {},
onConnectTraktRequested: () -> Unit = {},
onConnectMalRequested: () -> Unit = {},
onConnectSimklRequested: () -> Unit = {},
onConnectAnilistRequested: () -> Unit = {},
onCheckForUpdateRequested: () -> Unit = {},
@ -109,6 +111,9 @@ fun FluxaAppHost(
authStartOnNuvio = authStartOnNuvio,
nuvioIcon = nuvioIcon,
stremioIcon = stremioIcon,
traktIcon = traktIcon,
simklIcon = simklIcon,
anilistIcon = anilistIcon,
biometricAvailable = biometricAvailable,
onPickAvatarRequested = onPickAvatarRequested,
onBiometricAuthRequested = onBiometricAuthRequested,
@ -116,7 +121,6 @@ fun FluxaAppHost(
onConnectStremioRequested = onConnectStremioRequested,
onConnectNuvioRequested = onConnectNuvioRequested,
onConnectTraktRequested = onConnectTraktRequested,
onConnectMalRequested = onConnectMalRequested,
onConnectSimklRequested = onConnectSimklRequested,
onConnectAnilistRequested = onConnectAnilistRequested,
onCheckForUpdateRequested = onCheckForUpdateRequested,
@ -150,6 +154,9 @@ fun FluxaAppHost(
authStartOnNuvio: Boolean = false,
nuvioIcon: @Composable () -> Unit = {},
stremioIcon: @Composable () -> Unit = {},
traktIcon: @Composable () -> Unit = {},
simklIcon: @Composable () -> Unit = {},
anilistIcon: @Composable () -> Unit = {},
biometricAvailable: Boolean = false,
onPickAvatarRequested: (onPicked: (String?) -> Unit) -> Unit = {},
onBiometricAuthRequested: (ProfileUiModel, onResult: (Boolean) -> Unit) -> Unit = { _, _ -> },
@ -157,7 +164,6 @@ fun FluxaAppHost(
onConnectStremioRequested: () -> Unit = {},
onConnectNuvioRequested: () -> Unit = {},
onConnectTraktRequested: () -> Unit = {},
onConnectMalRequested: () -> Unit = {},
onConnectSimklRequested: () -> Unit = {},
onConnectAnilistRequested: () -> Unit = {},
onCheckForUpdateRequested: () -> Unit = {},
@ -342,7 +348,6 @@ fun FluxaAppHost(
SettingsAction.ConnectStremioRequested -> onConnectStremioRequested()
SettingsAction.ConnectNuvioRequested -> onConnectNuvioRequested()
SettingsAction.ConnectTraktRequested -> onConnectTraktRequested()
SettingsAction.ConnectMalRequested -> onConnectMalRequested()
SettingsAction.ConnectSimklRequested -> onConnectSimklRequested()
SettingsAction.ConnectAnilistRequested -> onConnectAnilistRequested()
SettingsAction.CheckForUpdateRequested -> onCheckForUpdateRequested()
@ -352,6 +357,13 @@ fun FluxaAppHost(
},
onSwitchProfilesRequested = { appState.selectDestination(FluxaDestination.ProfileList) },
onSettingsBackRequested = onSettingsBackRequested,
settingsBrandIcons = com.fluxa.app.shared.feature.settings.SettingsBrandIcons(
stremio = stremioIcon,
nuvio = nuvioIcon,
trakt = traktIcon,
simkl = simklIcon,
anilist = anilistIcon
),
addonStoreState = addonStoreState,
onAddonStoreAction = { action ->
scope.launch {

View file

@ -12,7 +12,6 @@ data class SettingsAccountUiModel(
val avatarUrl: String? = null,
val isGuest: Boolean = true,
val hasTrakt: Boolean = false,
val hasMal: Boolean = false,
val hasSimkl: Boolean = false,
val hasAnilist: Boolean = false,
val hasAnySync: Boolean = false,
@ -205,7 +204,6 @@ sealed interface SettingsAction {
data object ConnectStremioRequested : SettingsAction
data object ConnectNuvioRequested : SettingsAction
data object ConnectTraktRequested : SettingsAction
data object ConnectMalRequested : SettingsAction
data object ConnectSimklRequested : SettingsAction
data object ConnectAnilistRequested : SettingsAction
data object DisconnectSyncRequested : SettingsAction

View file

@ -267,6 +267,7 @@ fun SettingsActionRow(
label: String,
value: String? = null,
destructive: Boolean = false,
icon: (@Composable () -> Unit)? = null,
onClick: () -> Unit
) {
Row(
@ -274,7 +275,21 @@ fun SettingsActionRow(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(label, color = if (destructive) FluxaColors.errorRed else Color.White, fontWeight = FontWeight.Medium)
Row(verticalAlignment = Alignment.CenterVertically) {
if (icon != null) {
Box(
modifier = Modifier
.size(32.dp)
.clip(RoundedCornerShape(9.dp))
.background(Color.White.copy(alpha = 0.08f)),
contentAlignment = Alignment.Center
) {
icon()
}
Spacer(Modifier.width(14.dp))
}
Text(label, color = if (destructive) FluxaColors.errorRed else Color.White, fontWeight = FontWeight.Medium)
}
if (value != null) {
Text(value, color = Color.White.copy(alpha = 0.5f), fontSize = 13.sp)
}

View file

@ -21,7 +21,7 @@ import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Extension
import androidx.compose.material.icons.filled.MenuBook
import androidx.compose.material.icons.filled.LibraryBooks
import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.PlayCircle
import androidx.compose.material.icons.filled.Tune
@ -41,6 +41,14 @@ import androidx.compose.ui.unit.sp
import com.fluxa.app.common.AppStrings
import com.fluxa.app.ui.catalog.FluxaColors
data class SettingsBrandIcons(
val stremio: @Composable () -> Unit = {},
val nuvio: @Composable () -> Unit = {},
val trakt: @Composable () -> Unit = {},
val simkl: @Composable () -> Unit = {},
val anilist: @Composable () -> Unit = {}
)
@Composable
fun SettingsScreen(
state: SettingsUiState,
@ -48,6 +56,7 @@ fun SettingsScreen(
onAction: (SettingsAction) -> Unit,
onSwitchProfilesRequested: () -> Unit,
onBackRequested: () -> Unit,
brandIcons: SettingsBrandIcons = SettingsBrandIcons(),
modifier: Modifier = Modifier
) {
var category by remember { mutableStateOf(SettingsCategory.Hub) }
@ -68,7 +77,7 @@ fun SettingsScreen(
onSwitchProfiles = onSwitchProfilesRequested,
onAction = onAction
)
SettingsCategory.Account -> SettingsAccountContent(state.account, lang, onAction)
SettingsCategory.Account -> SettingsAccountContent(state.account, lang, brandIcons, onAction)
SettingsCategory.General -> SettingsGeneralContent(state.general, lang, onAction)
SettingsCategory.Appearance -> SettingsAppearanceContent(state.appearance, lang, onAction, onNavigate = { category = it })
SettingsCategory.AppearanceHome -> SettingsAppearanceHomeContent(state.appearanceHome, lang, onAction)
@ -149,7 +158,7 @@ private fun SettingsHubContent(
SettingsNavRow(AppStrings.t(lang, "auto.playback"), icon = Icons.Filled.PlayCircle) { onNavigate(SettingsCategory.Playback) }
SettingsSectionHeader(AppStrings.t(lang, "settings.section_content"))
SettingsNavRow(AppStrings.t(lang, "auto.catalogs"), icon = Icons.Filled.MenuBook) { onNavigate(SettingsCategory.Content) }
SettingsNavRow(AppStrings.t(lang, "auto.catalogs"), icon = Icons.Filled.LibraryBooks) { onNavigate(SettingsCategory.Content) }
SettingsNavRow(AppStrings.t(lang, "auto.add_ons"), icon = Icons.Filled.Extension) { onNavigate(SettingsCategory.Addons) }
SettingsNavRow(AppStrings.t(lang, "auto.downloads"), icon = Icons.Filled.Download) { onNavigate(SettingsCategory.Downloads) }
@ -168,24 +177,29 @@ private fun SettingsHubContent(
}
@Composable
private fun SettingsAccountContent(model: SettingsAccountUiModel, lang: String?, onAction: (SettingsAction) -> Unit) {
private fun SettingsAccountContent(model: SettingsAccountUiModel, lang: String?, brandIcons: SettingsBrandIcons, onAction: (SettingsAction) -> Unit) {
SettingsSectionHeader(AppStrings.t(lang, "auto.account_sync"))
SettingsActionRow(AppStrings.t(lang, "brand.stremio"), value = if (!model.isGuest) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected")) {
onAction(SettingsAction.ConnectStremioRequested)
}
SettingsActionRow(AppStrings.t(lang, "brand.nuvio")) { onAction(SettingsAction.ConnectNuvioRequested) }
SettingsActionRow(AppStrings.t(lang, "brand.trakt"), value = if (model.hasTrakt) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected")) {
onAction(SettingsAction.ConnectTraktRequested)
}
SettingsActionRow(AppStrings.t(lang, "brand.myanimelist"), value = if (model.hasMal) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected")) {
onAction(SettingsAction.ConnectMalRequested)
}
SettingsActionRow(AppStrings.t(lang, "brand.simkl"), value = if (model.hasSimkl) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected")) {
onAction(SettingsAction.ConnectSimklRequested)
}
SettingsActionRow(AppStrings.t(lang, "brand.anilist"), value = if (model.hasAnilist) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected")) {
onAction(SettingsAction.ConnectAnilistRequested)
}
SettingsActionRow(
AppStrings.t(lang, "brand.stremio"),
value = if (!model.isGuest) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected"),
icon = brandIcons.stremio
) { onAction(SettingsAction.ConnectStremioRequested) }
SettingsActionRow(AppStrings.t(lang, "brand.nuvio"), icon = brandIcons.nuvio) { onAction(SettingsAction.ConnectNuvioRequested) }
SettingsActionRow(
AppStrings.t(lang, "brand.trakt"),
value = if (model.hasTrakt) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected"),
icon = brandIcons.trakt
) { onAction(SettingsAction.ConnectTraktRequested) }
SettingsActionRow(
AppStrings.t(lang, "brand.simkl"),
value = if (model.hasSimkl) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected"),
icon = brandIcons.simkl
) { onAction(SettingsAction.ConnectSimklRequested) }
SettingsActionRow(
AppStrings.t(lang, "brand.anilist"),
value = if (model.hasAnilist) AppStrings.t(lang, "auto.connected") else AppStrings.t(lang, "auto.not_connected"),
icon = brandIcons.anilist
) { onAction(SettingsAction.ConnectAnilistRequested) }
if (model.hasAnySync) {
SettingsActionRow(AppStrings.t(lang, "auto.disconnect"), destructive = true) { onAction(SettingsAction.DisconnectSyncRequested) }
}

View file

@ -37,7 +37,6 @@ class SettingsStore(
SettingsAction.ConnectStremioRequested -> Unit
SettingsAction.ConnectNuvioRequested -> Unit
SettingsAction.ConnectTraktRequested -> Unit
SettingsAction.ConnectMalRequested -> Unit
SettingsAction.ConnectSimklRequested -> Unit
SettingsAction.ConnectAnilistRequested -> Unit
SettingsAction.DisconnectSyncRequested -> dataSource.disconnectSync()