diff --git a/anilist/src/commonMain/graphql/ViewerMutation.graphql b/anilist/src/commonMain/graphql/ViewerMutation.graphql index ae09baf..9fe1b59 100644 --- a/anilist/src/commonMain/graphql/ViewerMutation.graphql +++ b/anilist/src/commonMain/graphql/ViewerMutation.graphql @@ -1,5 +1,5 @@ -mutation ViewerMutation($adult: Boolean, $color: String, $html: Boolean) { - UpdateUser(displayAdultContent: $adult, profileColor: $color) { +mutation ViewerMutation($adult: Boolean, $color: String, $html: Boolean, $title: UserTitleLanguage) { + UpdateUser(displayAdultContent: $adult, profileColor: $color, titleLanguage: $title) { id, name, about(asHtml: $html), @@ -10,7 +10,8 @@ mutation ViewerMutation($adult: Boolean, $color: String, $html: Boolean) { bannerImage, options { displayAdultContent, - profileColor + profileColor, + titleLanguage } } } \ No newline at end of file diff --git a/anilist/src/commonMain/graphql/ViewerQuery.graphql b/anilist/src/commonMain/graphql/ViewerQuery.graphql index a027579..afe5c17 100644 --- a/anilist/src/commonMain/graphql/ViewerQuery.graphql +++ b/anilist/src/commonMain/graphql/ViewerQuery.graphql @@ -10,7 +10,8 @@ query ViewerQuery($html: Boolean) { bannerImage, options { displayAdultContent, - profileColor + profileColor, + titleLanguage } } } \ No newline at end of file diff --git a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/User.kt b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/User.kt index 337f59d..6c4ab28 100644 --- a/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/User.kt +++ b/anilist/src/commonMain/kotlin/dev/datlag/aniflow/anilist/model/User.kt @@ -2,6 +2,7 @@ package dev.datlag.aniflow.anilist.model import dev.datlag.aniflow.anilist.ViewerMutation import dev.datlag.aniflow.anilist.ViewerQuery +import dev.datlag.aniflow.anilist.type.UserTitleLanguage import kotlinx.serialization.Serializable @Serializable @@ -12,7 +13,8 @@ data class User( val avatar: Avatar = Avatar(), val banner: String? = null, val displayAdultContent: Boolean = false, - val profileColor: String? = null + val profileColor: String? = null, + val titleLanguage: TitleLanguage? = null, ) { constructor(query: ViewerQuery.Viewer) : this( id = query.id, @@ -21,7 +23,8 @@ data class User( avatar = query.avatar.let(::Avatar), banner = query.bannerImage?.ifBlank { null }, displayAdultContent = query.options?.displayAdultContent ?: false, - profileColor = query.options?.profileColor?.ifBlank { null } + profileColor = query.options?.profileColor?.ifBlank { null }, + titleLanguage = TitleLanguage.fromUser(query.options?.titleLanguage) ) constructor(mutation: ViewerMutation.UpdateUser) : this( @@ -31,7 +34,8 @@ data class User( avatar = mutation.avatar.let(::Avatar), banner = mutation.bannerImage?.ifBlank { null }, displayAdultContent = mutation.options?.displayAdultContent ?: false, - profileColor = mutation.options?.profileColor?.ifBlank { null } + profileColor = mutation.options?.profileColor?.ifBlank { null }, + titleLanguage = TitleLanguage.fromUser(mutation.options?.titleLanguage) ) @Serializable @@ -49,4 +53,25 @@ data class User( large = mutation?.large?.ifBlank { null } ) } + + @Serializable + sealed interface TitleLanguage { + @Serializable + data object Romaji : TitleLanguage + + @Serializable + data object English : TitleLanguage + + @Serializable + data object Native : TitleLanguage + + companion object { + fun fromUser(user: UserTitleLanguage?): TitleLanguage? = when (user) { + UserTitleLanguage.ROMAJI, UserTitleLanguage.ROMAJI_STYLISED -> Romaji + UserTitleLanguage.ENGLISH, UserTitleLanguage.ENGLISH_STYLISED -> English + UserTitleLanguage.NATIVE, UserTitleLanguage.NATIVE_STYLISED -> Native + else -> null + } + } + } } diff --git a/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.android.kt b/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.android.kt index a5e464f..db18f5f 100644 --- a/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.android.kt +++ b/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.android.kt @@ -3,16 +3,34 @@ package dev.datlag.aniflow.common import dev.datlag.aniflow.anilist.TrendingQuery import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.anilist.model.Character +import dev.datlag.aniflow.settings.model.AppSettings import java.util.Locale -actual fun Medium.Title.preferred(): String { - return this.userPreferred?.ifBlank { null } ?: run { +actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { + val locale = Locale.getDefault() + val isJapanese = locale.language.equals("jp", ignoreCase = true) + || locale.language.equals("ja", ignoreCase = true) + || locale.isO3Language.equals("jpn", ignoreCase = true) - val locale = Locale.getDefault() - val isJapanese = locale.language.equals("jp", ignoreCase = true) - || locale.language.equals("ja", ignoreCase = true) - || locale.isO3Language.equals("jpn", ignoreCase = true) + if (setting != null) { + return when (setting) { + is AppSettings.TitleLanguage.Romaji -> this.romaji?.ifBlank { null } ?: if (isJapanese) { + this.native?.ifBlank { null } ?: this.english?.ifBlank { null } + } else { + this.english?.ifBlank { null } ?: this.native?.ifBlank { null } + } ?: "" + is AppSettings.TitleLanguage.English -> this.english?.ifBlank { null } + ?: this.romaji?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + is AppSettings.TitleLanguage.Native -> this.native?.ifBlank { null } + ?: this.romaji?.ifBlank { null } + ?: this.english?.ifBlank { null } + ?: "" + } + } + return this.userPreferred?.ifBlank { null } ?: run { if (isJapanese) { this.native?.ifBlank { null } ?: this.romaji?.ifBlank { null } diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt index 22b26a5..8a901ab 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt @@ -1,27 +1,45 @@ package dev.datlag.aniflow.common import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import dev.datlag.aniflow.LocalDI import dev.datlag.aniflow.SharedRes import dev.datlag.aniflow.anilist.model.Character import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.anilist.type.MediaFormat import dev.datlag.aniflow.anilist.type.MediaRankType import dev.datlag.aniflow.anilist.type.MediaStatus +import dev.datlag.aniflow.settings.Settings +import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.aniflow.trace.model.SearchResponse +import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import dev.icerock.moko.resources.StringResource +import org.kodein.di.instance -fun Medium.preferred(): String { - return this.title.preferred().ifBlank { this.id.toString() } +@Composable +fun Medium.preferred(setting: AppSettings.TitleLanguage? = null): String { + val appSettings by LocalDI.current.instance() + val title by appSettings.titleLanguage.collectAsStateWithLifecycle(null) + + return this.title.preferred(setting ?: title).ifBlank { this.id.toString() } } -fun Medium.notPreferred(): String? { - return this.title.notPreferred()?.ifBlank { null } +@Composable +fun Medium.notPreferred(setting: AppSettings.TitleLanguage? = null): String? { + val appSettings by LocalDI.current.instance() + val title by appSettings.titleLanguage.collectAsStateWithLifecycle(null) + + return this.title.notPreferred(setting ?: title)?.ifBlank { null } } -expect fun Medium.Title.preferred(): String +expect fun Medium.Title.preferred(setting: AppSettings.TitleLanguage? = null): String + +@Composable +fun Medium.Title.notPreferred(setting: AppSettings.TitleLanguage? = null): String? { + val appSettings by LocalDI.current.instance() + val title by appSettings.titleLanguage.collectAsStateWithLifecycle(null) -fun Medium.Title.notPreferred(): String? { - val preferred = this.preferred().trim() + val preferred = this.preferred(setting ?: title).trim() val notPreferred = when { this.native?.trim().equals(preferred, ignoreCase = true) -> { when { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt index 4f00d62..166daa0 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt @@ -2,6 +2,8 @@ package dev.datlag.aniflow.common import androidx.compose.ui.graphics.Color import dev.datlag.aniflow.SharedRes +import dev.datlag.aniflow.anilist.model.User +import dev.datlag.aniflow.anilist.type.UserTitleLanguage import dev.icerock.moko.resources.StringResource import dev.datlag.aniflow.settings.model.AppSettings @@ -19,4 +21,24 @@ fun AppSettings.Color.toComposeString(): StringResource = when (this) { is AppSettings.Color.Green -> SharedRes.strings.color_green is AppSettings.Color.Gray -> SharedRes.strings.color_gray is AppSettings.Color.Custom -> SharedRes.strings.color_custom +} + +fun User.TitleLanguage?.toSettings(): AppSettings.TitleLanguage? = when (this) { + is User.TitleLanguage.Romaji -> AppSettings.TitleLanguage.Romaji + is User.TitleLanguage.English -> AppSettings.TitleLanguage.English + is User.TitleLanguage.Native -> AppSettings.TitleLanguage.Native + else -> null +} + +fun AppSettings.TitleLanguage?.toMutation(): UserTitleLanguage? = when (this) { + is AppSettings.TitleLanguage.Romaji -> UserTitleLanguage.ROMAJI + is AppSettings.TitleLanguage.English -> UserTitleLanguage.ENGLISH + is AppSettings.TitleLanguage.Native -> UserTitleLanguage.NATIVE + else -> null +} + +fun AppSettings.TitleLanguage.toComposeString(): StringResource = when (this) { + is AppSettings.TitleLanguage.Romaji -> SharedRes.strings.title_romaji + is AppSettings.TitleLanguage.English -> SharedRes.strings.title_english + is AppSettings.TitleLanguage.Native -> SharedRes.strings.title_native } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt index 06abd40..9f33ddc 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt @@ -5,10 +5,8 @@ import dev.datlag.aniflow.anilist.PopularNextSeasonStateMachine import dev.datlag.aniflow.anilist.PopularSeasonStateMachine import dev.datlag.aniflow.anilist.TrendingAnimeStateMachine import dev.datlag.aniflow.anilist.state.SeasonState -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet +import dev.datlag.tooling.compose.ioDispatcher +import kotlinx.coroutines.flow.* data object StateSaver { var sekretLibraryLoaded: Boolean = false @@ -69,7 +67,9 @@ data object StateSaver { _popularNextState ) { t1, t2, t3, t4 -> t1.isLoadingOrWaiting && t2.isLoadingOrWaiting && t3.isLoadingOrWaiting && t4.isLoadingOrWaiting - } + }.flowOn( + context = ioDispatcher() + ) fun updateAiring(state: AiringTodayStateMachine.State) = _airingState.updateAndGet { state diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt index d560ca7..d765c0f 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt @@ -5,6 +5,8 @@ import com.apollographql.apollo3.api.Optional import dev.datlag.aniflow.anilist.ViewerMutation import dev.datlag.aniflow.anilist.ViewerQuery import dev.datlag.aniflow.anilist.model.User +import dev.datlag.aniflow.common.toMutation +import dev.datlag.aniflow.common.toSettings import dev.datlag.aniflow.model.safeFirstOrNull import dev.datlag.aniflow.settings.Settings import dev.datlag.aniflow.settings.model.AppSettings @@ -56,7 +58,8 @@ class UserHelper( user?.also { appSettings.setData( adultContent = it.displayAdultContent, - color = AppSettings.Color.fromString(it.profileColor) + color = AppSettings.Color.fromString(it.profileColor), + titleLanguage = it.titleLanguage.toSettings() ) } ) @@ -109,6 +112,21 @@ class UserHelper( } } + suspend fun updateTitleLanguage(value: AppSettings.TitleLanguage?) { + appSettings.setTitleLanguage(value) + + if (value != null) { + changedUser.emit( + client.mutation( + ViewerMutation( + title = Optional.presentIfNotNull(value.toMutation()), + html = Optional.present(true) + ) + ).execute().data?.UpdateUser?.let(::User) + ) + } + } + private suspend fun updateStoredToken(tokenResponse: AccessTokenResponse) { userSettings.setAniListTokens( access = tokenResponse.access_token, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt index 853f4b9..cbf4273 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt @@ -5,6 +5,7 @@ import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -14,10 +15,16 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import coil3.compose.rememberAsyncImagePainter +import dev.datlag.aniflow.LocalDI import dev.datlag.aniflow.anilist.AiringQuery import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.common.preferred +import dev.datlag.aniflow.settings.Settings +import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.aniflow.ui.theme.SchemeTheme +import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle +import org.kodein.di.instance +import org.kodein.di.instanceOrNull @Composable fun AiringCard( diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsComponent.kt index f29b154..d136b18 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsComponent.kt @@ -9,7 +9,9 @@ interface SettingsComponent : Component { val user: Flow val adultContent: Flow val selectedColor: Flow + val selectedTitleLanguage: Flow fun changeAdultContent(value: Boolean) fun changeProfileColor(value: AppSettings.Color?) + fun changeTitleLanguage(value: AppSettings.TitleLanguage?) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreen.kt index 7ef33bc..d55422e 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreen.kt @@ -7,7 +7,9 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.More import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.rounded.Title import androidx.compose.material3.* import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass @@ -101,37 +103,6 @@ fun SettingsScreen(component: SettingsComponent) { fontWeight = FontWeight.Bold ) } - item { - val adultContent by component.adultContent.collectAsStateWithLifecycle(false) - - Row( - modifier = Modifier.fillParentMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Icon( - imageVector = Icons.Default.NoAdultContent, - contentDescription = null, - ) - Text( - text = stringResource(SharedRes.strings.adult_content_setting) - ) - Spacer(modifier = Modifier.weight(1F)) - Switch( - checked = adultContent, - onCheckedChange = component::changeAdultContent, - thumbContent = { - if (adultContent) { - Icon( - modifier = Modifier.size(SwitchDefaults.IconSize), - imageVector = Icons.Default.Check, - contentDescription = null - ) - } - } - ) - } - } item { val selectedColor by component.selectedColor.collectAsStateWithLifecycle(null) val useCase = rememberUseCaseState() @@ -184,6 +155,83 @@ fun SettingsScreen(component: SettingsComponent) { } } } + item { + val selectedTitle by component.selectedTitleLanguage.collectAsStateWithLifecycle(null) + val useCase = rememberUseCaseState() + val languages = remember { AppSettings.TitleLanguage.all.toList() } + + OptionDialog( + state = useCase, + selection = OptionSelection.Single( + options = languages.map { + Option( + selected = it == selectedTitle, + titleText = stringResource(it.toComposeString()) + ) + }, + onSelectOption = { option, _ -> + component.changeTitleLanguage(languages[option]) + } + ), + config = OptionConfig( + mode = DisplayMode.LIST, + ) + ) + + Row( + modifier = Modifier.fillParentMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + imageVector = Icons.Rounded.Title, + contentDescription = null + ) + Text( + text = "Title Language" + ) + Spacer(modifier = Modifier.weight(1F)) + IconButton( + onClick = { useCase.show() } + ) { + Icon( + imageVector = Icons.Default.ExpandMore, + contentDescription = null + ) + } + } + } + item { + val adultContent by component.adultContent.collectAsStateWithLifecycle(false) + + Row( + modifier = Modifier.fillParentMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + imageVector = Icons.Default.NoAdultContent, + contentDescription = null, + ) + Text( + text = stringResource(SharedRes.strings.adult_content_setting) + ) + Spacer(modifier = Modifier.weight(1F)) + Switch( + checked = adultContent, + onCheckedChange = component::changeAdultContent, + thumbContent = { + if (adultContent) { + Icon( + modifier = Modifier.size(SwitchDefaults.IconSize), + imageVector = Icons.Default.Check, + contentDescription = null + ) + } + } + ) + } + } } DisposableEffect(listState) { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreenComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreenComponent.kt index 2bfdd82..b95d13b 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreenComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/settings/SettingsScreenComponent.kt @@ -24,6 +24,7 @@ class SettingsScreenComponent( override val user: Flow = userHelper.user.flowOn(ioDispatcher()) override val adultContent: Flow = appSettings.adultContent.flowOn(ioDispatcher()) override val selectedColor: Flow = appSettings.color.flowOn(ioDispatcher()) + override val selectedTitleLanguage: Flow = appSettings.titleLanguage.flowOn(ioDispatcher()) @Composable override fun render() { @@ -43,4 +44,10 @@ class SettingsScreenComponent( userHelper.updateProfileColorSetting(value) } } + + override fun changeTitleLanguage(value: AppSettings.TitleLanguage?) { + launchIO { + userHelper.updateTitleLanguage(value) + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/moko-resources/base/strings.xml b/composeApp/src/commonMain/moko-resources/base/strings.xml index 24c1117..777344a 100644 --- a/composeApp/src/commonMain/moko-resources/base/strings.xml +++ b/composeApp/src/commonMain/moko-resources/base/strings.xml @@ -48,4 +48,7 @@ Similarity Maximum: %s\nAverage: %s Average: %s + Romaji + English + Native diff --git a/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.ios.kt b/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.ios.kt index 96a9642..4247f3a 100644 --- a/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.ios.kt +++ b/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.ios.kt @@ -3,8 +3,27 @@ package dev.datlag.aniflow.common import dev.datlag.aniflow.anilist.TrendingQuery import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.anilist.model.Character +import dev.datlag.aniflow.settings.model.AppSettings + +actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { + + if (setting != null) { + return when (setting) { + is AppSettings.TitleLanguage.Romaji -> this.romaji?.ifBlank { null } + ?: this.english?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + is AppSettings.TitleLanguage.English -> this.english?.ifBlank { null } + ?: this.romaji?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + is AppSettings.TitleLanguage.Native -> this.native?.ifBlank { null } + ?: this.romaji?.ifBlank { null } + ?: this.english?.ifBlank { null } + ?: "" + } + } -actual fun Medium.Title.preferred(): String { return this.userPreferred?.ifBlank { null } ?: this.english?.ifBlank { null } ?: this.romaji?.ifBlank { null } diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt index 7624e71..0a5ba9d 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt @@ -12,7 +12,8 @@ import okio.BufferedSource data object AppSettingsSerializer : OkioSerializer { override val defaultValue: AppSettings = AppSettings( - color = null + color = null, + titleLanguage = null ) @OptIn(ExperimentalSerializationApi::class) diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt index ad870d2..41924b8 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt @@ -10,6 +10,7 @@ class DataStoreAppSettings( ) : Settings.PlatformAppSettings { override val adultContent: Flow = dateStore.data.map { it.adultContent } override val color: Flow = dateStore.data.map { it.color } + override val titleLanguage: Flow = dateStore.data.map { it.titleLanguage } override suspend fun setAdultContent(value: Boolean) { dateStore.updateData { @@ -27,11 +28,24 @@ class DataStoreAppSettings( } } - override suspend fun setData(adultContent: Boolean, color: AppSettings.Color?) { + override suspend fun setTitleLanguage(value: AppSettings.TitleLanguage?) { + dateStore.updateData { + it.copy( + titleLanguage = value + ) + } + } + + override suspend fun setData( + adultContent: Boolean, + color: AppSettings.Color?, + titleLanguage: AppSettings.TitleLanguage?, + ) { dateStore.updateData { it.copy( adultContent = adultContent, - color = color + color = color, + titleLanguage = titleLanguage, ) } } diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt index 2d8655b..70038f1 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt @@ -20,15 +20,18 @@ data object Settings { interface PlatformAppSettings { val adultContent: Flow val color: Flow + val titleLanguage: Flow suspend fun setAdultContent(value: Boolean) suspend fun setColor(value: AppSettings.Color?) suspend fun setColor(value: String?) = setColor(value?.let { AppSettings.Color.fromString(it) }) + suspend fun setTitleLanguage(value: AppSettings.TitleLanguage?) suspend fun setData( adultContent: Boolean, - color: AppSettings.Color? + color: AppSettings.Color?, + titleLanguage: AppSettings.TitleLanguage?, ) } } \ No newline at end of file diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/AppSettings.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/AppSettings.kt index 7be55da..b5353de 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/AppSettings.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/AppSettings.kt @@ -15,6 +15,7 @@ import kotlinx.serialization.protobuf.ProtoNumber data class AppSettings( @ProtoNumber(1) val adultContent: Boolean = false, @ProtoNumber(2) val color: Color?, + @ProtoNumber(3) val titleLanguage: TitleLanguage?, ) { @Serializable(with = Color.ColorSerializer::class) @@ -119,4 +120,58 @@ data class AppSettings( } } } + + @Serializable(with = TitleLanguage.TitleSerializer::class) + sealed interface TitleLanguage { + val id: Int + + @Serializable + data object Romaji : TitleLanguage { + override val id: Int = 1 + } + + @Serializable + data object English : TitleLanguage { + override val id: Int = 2 + } + + @Serializable + data object Native : TitleLanguage { + override val id: Int = 3 + } + + companion object TitleSerializer : KSerializer { + val all: Set = setOf( + Romaji, + English, + Native + ) + + internal fun fromId(value: Int): TitleLanguage? = when (value) { + 1 -> Romaji + 2 -> English + 3 -> Native + else -> null + } + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Title", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): TitleLanguage? { + return if (decoder.decodeNotNullMark()) { + fromId(decoder.decodeInt()) + } else { + decoder.decodeNull() + } + } + + override fun serialize(encoder: Encoder, value: TitleLanguage?) { + if (value != null) { + encoder.encodeNotNullMark() + encoder.encodeInt(value.id) + } else { + encoder.encodeNull() + } + } + } + } }