From 7f9d851d95c75c57ee769db9a057b117dfda9204 Mon Sep 17 00:00:00 2001 From: DatLag Date: Wed, 1 May 2024 14:53:39 +0200 Subject: [PATCH] added character language option --- .../commonMain/graphql/ViewerMutation.graphql | 13 +- .../commonMain/graphql/ViewerQuery.graphql | 3 +- .../dev/datlag/aniflow/anilist/model/User.kt | 29 ++- .../aniflow/common/ExtendMedium.android.kt | 63 +++++-- .../dev/datlag/aniflow/common/ExtendMedium.kt | 18 +- .../datlag/aniflow/common/ExtendSettings.kt | 69 +++++--- .../dev/datlag/aniflow/other/StateSaver.kt | 5 +- .../dev/datlag/aniflow/other/UserHelper.kt | 28 ++- .../screen/initial/home/HomeComponent.kt | 3 +- .../initial/home/HomeScreenComponent.kt | 3 +- .../initial/home/component/AiringCard.kt | 3 +- .../initial/home/component/AiringOverview.kt | 5 +- .../initial/home/component/MediumCard.kt | 3 +- .../home/component/PopularSeasonOverview.kt | 5 +- .../home/component/TrendingOverview.kt | 5 +- .../initial/settings/SettingsComponent.kt | 14 +- .../screen/initial/settings/SettingsScreen.kt | 69 ++++++-- .../settings/SettingsScreenComponent.kt | 25 ++- .../screen/medium/MediumComponent.kt | 5 +- .../navigation/screen/medium/MediumScreen.kt | 4 +- .../screen/medium/MediumScreenComponent.kt | 5 +- .../screen/medium/component/CharacterCard.kt | 8 +- .../medium/component/CharacterSection.kt | 3 + .../medium/component/CollapsingToolbar.kt | 3 +- .../dialog/character/CharacterComponent.kt | 2 + .../dialog/character/CharacterDialog.kt | 5 +- .../character/CharacterDialogComponent.kt | 6 + .../moko-resources/base/strings.xml | 3 + .../datlag/aniflow/common/ExtendMedium.ios.kt | 59 +++++-- .../dev/datlag/aniflow/model/ExtendAny.kt | 11 +- .../aniflow/settings/AppSettingsSerializer.kt | 3 +- .../aniflow/settings/DataStoreAppSettings.kt | 26 ++- .../dev/datlag/aniflow/settings/Settings.kt | 20 ++- .../aniflow/settings/model/AppSettings.kt | 167 +----------------- .../aniflow/settings/model/CharLanguage.kt | 66 +++++++ .../datlag/aniflow/settings/model/Color.kt | 115 ++++++++++++ .../aniflow/settings/model/TitleLanguage.kt | 66 +++++++ 37 files changed, 637 insertions(+), 303 deletions(-) create mode 100644 settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/CharLanguage.kt create mode 100644 settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/Color.kt create mode 100644 settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/TitleLanguage.kt diff --git a/anilist/src/commonMain/graphql/ViewerMutation.graphql b/anilist/src/commonMain/graphql/ViewerMutation.graphql index 9fe1b59..abe7586 100644 --- a/anilist/src/commonMain/graphql/ViewerMutation.graphql +++ b/anilist/src/commonMain/graphql/ViewerMutation.graphql @@ -1,5 +1,11 @@ -mutation ViewerMutation($adult: Boolean, $color: String, $html: Boolean, $title: UserTitleLanguage) { - UpdateUser(displayAdultContent: $adult, profileColor: $color, titleLanguage: $title) { +mutation ViewerMutation( + $adult: Boolean, + $color: String, + $html: Boolean, + $title: UserTitleLanguage, + $char: UserStaffNameLanguage +) { + UpdateUser(displayAdultContent: $adult, profileColor: $color, titleLanguage: $title, staffNameLanguage: $char) { id, name, about(asHtml: $html), @@ -11,7 +17,8 @@ mutation ViewerMutation($adult: Boolean, $color: String, $html: Boolean, $title: options { displayAdultContent, profileColor, - titleLanguage + titleLanguage, + staffNameLanguage } } } \ No newline at end of file diff --git a/anilist/src/commonMain/graphql/ViewerQuery.graphql b/anilist/src/commonMain/graphql/ViewerQuery.graphql index afe5c17..15eb7d6 100644 --- a/anilist/src/commonMain/graphql/ViewerQuery.graphql +++ b/anilist/src/commonMain/graphql/ViewerQuery.graphql @@ -11,7 +11,8 @@ query ViewerQuery($html: Boolean) { options { displayAdultContent, profileColor, - titleLanguage + titleLanguage, + staffNameLanguage } } } \ 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 6c4ab28..bb2a56c 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.UserStaffNameLanguage import dev.datlag.aniflow.anilist.type.UserTitleLanguage import kotlinx.serialization.Serializable @@ -15,6 +16,7 @@ data class User( val displayAdultContent: Boolean = false, val profileColor: String? = null, val titleLanguage: TitleLanguage? = null, + val charLanguage: CharLanguage? = null ) { constructor(query: ViewerQuery.Viewer) : this( id = query.id, @@ -24,7 +26,8 @@ data class User( banner = query.bannerImage?.ifBlank { null }, displayAdultContent = query.options?.displayAdultContent ?: false, profileColor = query.options?.profileColor?.ifBlank { null }, - titleLanguage = TitleLanguage.fromUser(query.options?.titleLanguage) + titleLanguage = TitleLanguage.fromUser(query.options?.titleLanguage), + charLanguage = CharLanguage.fromUser(query.options?.staffNameLanguage) ) constructor(mutation: ViewerMutation.UpdateUser) : this( @@ -35,7 +38,8 @@ data class User( banner = mutation.bannerImage?.ifBlank { null }, displayAdultContent = mutation.options?.displayAdultContent ?: false, profileColor = mutation.options?.profileColor?.ifBlank { null }, - titleLanguage = TitleLanguage.fromUser(mutation.options?.titleLanguage) + titleLanguage = TitleLanguage.fromUser(mutation.options?.titleLanguage), + charLanguage = CharLanguage.fromUser(mutation.options?.staffNameLanguage) ) @Serializable @@ -74,4 +78,25 @@ data class User( } } } + + @Serializable + sealed interface CharLanguage { + @Serializable + data object RomajiWestern : CharLanguage + + @Serializable + data object Romaji : CharLanguage + + @Serializable + data object Native : CharLanguage + + companion object { + fun fromUser(user: UserStaffNameLanguage?): CharLanguage? = when (user) { + UserStaffNameLanguage.ROMAJI_WESTERN-> RomajiWestern + UserStaffNameLanguage.ROMAJI -> Romaji + UserStaffNameLanguage.NATIVE -> 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 db18f5f..4845c54 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 @@ -5,8 +5,11 @@ 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 +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar +import dev.datlag.aniflow.model.appendWithSpace -actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { +actual fun Medium.Title.preferred(setting: SettingsTitle?): String { val locale = Locale.getDefault() val isJapanese = locale.language.equals("jp", ignoreCase = true) || locale.language.equals("ja", ignoreCase = true) @@ -14,16 +17,16 @@ actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { if (setting != null) { return when (setting) { - is AppSettings.TitleLanguage.Romaji -> this.romaji?.ifBlank { null } ?: if (isJapanese) { + is SettingsTitle.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 } + is SettingsTitle.English -> this.english?.ifBlank { null } ?: this.romaji?.ifBlank { null } ?: this.native?.ifBlank { null } ?: "" - is AppSettings.TitleLanguage.Native -> this.native?.ifBlank { null } + is SettingsTitle.Native -> this.native?.ifBlank { null } ?: this.romaji?.ifBlank { null } ?: this.english?.ifBlank { null } ?: "" @@ -43,7 +46,41 @@ actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { } ?: "" } -actual fun Character.Name.preferred(): String { +actual fun Character.Name.preferred(setting: SettingsChar?): String { + if (setting != null) { + return when (setting) { + is SettingsChar.RomajiWestern -> buildString { + appendWithSpace(this@preferred.first) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.last) + }.trim().ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: this.full?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + + is SettingsChar.Romaji -> buildString { + appendWithSpace(this@preferred.last) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.first) + }.trim().ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: this.full?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + + is SettingsChar.Native -> this.native?.ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: buildString { + appendWithSpace(this@preferred.last) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.first) + }.trim().ifBlank { null } + ?: this.full?.ifBlank { null } + ?: "" + } + } + return this.userPreferred?.ifBlank { null } ?: run { val locale = Locale.getDefault() @@ -55,20 +92,16 @@ actual fun Character.Name.preferred(): String { this.native?.ifBlank { null } ?: this.full?.ifBlank { null } ?: buildString { - append(this@preferred.first) - append(" ") - append(this@preferred.middle) - append(" ") - append(this@preferred.last) + appendWithSpace(this@preferred.first) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.last) }.ifBlank { null } } else { this.full?.ifBlank { null } ?: buildString { - append(this@preferred.first) - append(" ") - append(this@preferred.middle) - append(" ") - append(this@preferred.last) + appendWithSpace(this@preferred.first) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.last) }.ifBlank { null } ?: this.native } 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 8422502..1713b87 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendMedium.kt @@ -15,18 +15,20 @@ 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 +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar -fun Medium.preferred(setting: AppSettings.TitleLanguage? = null): String { +fun Medium.preferred(setting: SettingsTitle?): String { return this.title.preferred(setting).ifBlank { this.id.toString() } } -fun Medium.notPreferred(setting: AppSettings.TitleLanguage? = null): String? { +fun Medium.notPreferred(setting: SettingsTitle?): String? { return this.title.notPreferred(setting)?.ifBlank { null } } -expect fun Medium.Title.preferred(setting: AppSettings.TitleLanguage? = null): String +expect fun Medium.Title.preferred(setting: SettingsTitle?): String -fun Medium.Title.notPreferred(setting: AppSettings.TitleLanguage? = null): String? { +fun Medium.Title.notPreferred(setting: SettingsTitle?): String? { val preferred = this.preferred(setting).trim() val notPreferred = when { this.native?.trim().equals(preferred, ignoreCase = true) -> { @@ -127,9 +129,9 @@ fun Collection.popular(): Medium.Ranking? { fun Medium.popular(): Medium.Ranking? = this.ranking.popular() -expect fun Character.Name.preferred(): String +expect fun Character.Name.preferred(setting: SettingsChar?): String -fun Character.preferredName(): String = this.name.preferred() +fun Character.preferredName(settings: SettingsChar?): String = this.name.preferred(settings) private fun SearchResponse.Result.AniList.Title?.asMediumTitle(): Medium.Title { return Medium.Title( @@ -147,6 +149,4 @@ fun SearchResponse.Result.AniList.asMedium(): Medium { _isAdult = this.isAdult, title = this.title.asMediumTitle() ) -} - -fun SearchResponse.Result.AniList.Title.preferred(): String = this.asMediumTitle().preferred() \ No newline at end of file +} \ No newline at end of file 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 166daa0..fad0066 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendSettings.kt @@ -3,42 +3,65 @@ 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.UserStaffNameLanguage import dev.datlag.aniflow.anilist.type.UserTitleLanguage import dev.icerock.moko.resources.StringResource -import dev.datlag.aniflow.settings.model.AppSettings +import dev.datlag.aniflow.settings.model.Color as SettingsColor +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar @OptIn(ExperimentalStdlibApi::class) -fun AppSettings.Color.toComposeColor() = Color( +fun SettingsColor.toComposeColor() = Color( this.hex.substringAfter('#').hexToLong() or 0x00000000FF000000 ) -fun AppSettings.Color.toComposeString(): StringResource = when (this) { - is AppSettings.Color.Blue -> SharedRes.strings.color_blue - is AppSettings.Color.Purple -> SharedRes.strings.color_purple - is AppSettings.Color.Pink -> SharedRes.strings.color_pink - is AppSettings.Color.Orange -> SharedRes.strings.color_orange - is AppSettings.Color.Red -> SharedRes.strings.color_red - 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 SettingsColor.toComposeString(): StringResource = when (this) { + is SettingsColor.Blue -> SharedRes.strings.color_blue + is SettingsColor.Purple -> SharedRes.strings.color_purple + is SettingsColor.Pink -> SharedRes.strings.color_pink + is SettingsColor.Orange -> SharedRes.strings.color_orange + is SettingsColor.Red -> SharedRes.strings.color_red + is SettingsColor.Green -> SharedRes.strings.color_green + is SettingsColor.Gray -> SharedRes.strings.color_gray + is SettingsColor.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 +fun User.TitleLanguage?.toSettings(): SettingsTitle? = when (this) { + is User.TitleLanguage.Romaji -> SettingsTitle.Romaji + is User.TitleLanguage.English -> SettingsTitle.English + is User.TitleLanguage.Native -> SettingsTitle.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 +fun SettingsTitle?.toMutation(): UserTitleLanguage? = when (this) { + is SettingsTitle.Romaji -> UserTitleLanguage.ROMAJI + is SettingsTitle.English -> UserTitleLanguage.ENGLISH + is SettingsTitle.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 +fun SettingsTitle.toComposeString(): StringResource = when (this) { + is SettingsTitle.Romaji -> SharedRes.strings.title_romaji + is SettingsTitle.English -> SharedRes.strings.title_english + is SettingsTitle.Native -> SharedRes.strings.title_native +} + +fun User.CharLanguage?.toSettings(): SettingsChar? = when (this) { + is User.CharLanguage.RomajiWestern -> SettingsChar.RomajiWestern + is User.CharLanguage.Romaji -> SettingsChar.Romaji + is User.CharLanguage.Native -> SettingsChar.Native + else -> null +} + +fun SettingsChar?.toMutation(): UserStaffNameLanguage? = when (this) { + is SettingsChar.RomajiWestern -> UserStaffNameLanguage.ROMAJI_WESTERN + is SettingsChar.Romaji -> UserStaffNameLanguage.ROMAJI + is SettingsChar.Native -> UserStaffNameLanguage.NATIVE + else -> null +} + +fun SettingsChar.toComposeString(): StringResource = when (this) { + is SettingsChar.RomajiWestern -> SharedRes.strings.char_romaji_western + is SettingsChar.Romaji -> SharedRes.strings.char_romaji + is SettingsChar.Native -> SharedRes.strings.char_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 f8f3579..3c93f92 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt @@ -11,12 +11,13 @@ import dev.datlag.tooling.compose.ioDispatcher import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlin.time.Duration.Companion.milliseconds +import dev.datlag.aniflow.settings.model.Color as SettingsColor data object StateSaver { var sekretLibraryLoaded: Boolean = false - val temporaryColor = MutableStateFlow(null) + val temporaryColor = MutableStateFlow(null) - fun updateTemporaryColor(value: AppSettings.Color?) = temporaryColor.update { value } + fun updateTemporaryColor(value: SettingsColor?) = temporaryColor.update { value } data object List { var homeOverview: Int = 0 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 759da06..611482a 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/UserHelper.kt @@ -13,7 +13,6 @@ 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 import dev.datlag.tooling.async.suspendCatching import dev.datlag.tooling.compose.withIOContext import dev.datlag.tooling.compose.withMainContext @@ -22,6 +21,9 @@ import kotlinx.coroutines.runBlocking import kotlinx.datetime.Clock import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds +import dev.datlag.aniflow.settings.model.Color as SettingsColor +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar class UserHelper( private val userSettings: Settings.PlatformUserSettings, @@ -63,8 +65,9 @@ class UserHelper( user?.also { appSettings.setData( adultContent = it.displayAdultContent, - color = AppSettings.Color.fromString(it.profileColor), - titleLanguage = it.titleLanguage.toSettings() + color = SettingsColor.fromString(it.profileColor), + titleLanguage = it.titleLanguage.toSettings(), + charLanguage = it.charLanguage.toSettings() ) } ) @@ -82,7 +85,7 @@ class UserHelper( ) } - suspend fun updateProfileColorSetting(value: AppSettings.Color?) { + suspend fun updateProfileColorSetting(value: SettingsColor?) { appSettings.setColor(value) if (value != null) { @@ -97,7 +100,7 @@ class UserHelper( } } - suspend fun updateTitleLanguage(value: AppSettings.TitleLanguage?) { + suspend fun updateTitleLanguage(value: SettingsTitle?) { appSettings.setTitleLanguage(value) if (value != null) { @@ -112,6 +115,21 @@ class UserHelper( } } + suspend fun updateCharLanguage(value: SettingsChar?) { + appSettings.setCharLanguage(value) + + if (value != null) { + changedUser.emit( + client.mutation( + ViewerMutation( + char = Optional.presentIfNotNull(value.toMutation()), + html = Optional.present(true) + ) + ).execute().data?.UpdateUser?.let(::User) + ) + } + } + suspend fun saveLogin( accessToken: String, expiresIn: Int?, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeComponent.kt index 6b6db21..35f220e 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeComponent.kt @@ -15,9 +15,10 @@ import io.ktor.utils.io.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle interface HomeComponent : ContentHolderComponent { - val titleLanguage: Flow + val titleLanguage: Flow val airingState: Flow val trendingState: Flow diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeScreenComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeScreenComponent.kt index f27b913..719fe01 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeScreenComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/HomeScreenComponent.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.* import org.kodein.di.DI import org.kodein.di.instance import kotlin.time.Duration.Companion.seconds +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle class HomeScreenComponent( componentContext: ComponentContext, @@ -36,7 +37,7 @@ class HomeScreenComponent( ) : HomeComponent, ComponentContext by componentContext { private val appSettings by di.instance() - override val titleLanguage: Flow = appSettings.titleLanguage + override val titleLanguage: Flow = appSettings.titleLanguage private val airingTodayStateMachine by di.instance() override val airingState: Flow = airingTodayStateMachine.state.map { 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 47851de..dd290ac 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 @@ -28,11 +28,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import org.kodein.di.instance import org.kodein.di.instanceOrNull +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @Composable fun AiringCard( airing: AiringQuery.AiringSchedule, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, modifier: Modifier = Modifier, onClick: (Medium) -> Unit ) { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringOverview.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringOverview.kt index 1db3714..a6d359d 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringOverview.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringOverview.kt @@ -25,11 +25,12 @@ import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @Composable fun AiringOverview( state: Flow, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit ) { val loadingState by state.collectAsStateWithLifecycle(StateSaver.Home.airingState) @@ -67,7 +68,7 @@ private fun Loading() { @Composable private fun SuccessContent( data: List, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit ) { val state = rememberLazyListState( diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt index 69813c1..d58ebf1 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt @@ -24,12 +24,13 @@ import dev.datlag.aniflow.ui.theme.SchemeTheme import dev.datlag.aniflow.ui.theme.rememberSchemeThemeDominantColorState import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import kotlinx.coroutines.flow.Flow +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @OptIn(ExperimentalStdlibApi::class) @Composable fun MediumCard( medium: Medium, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, modifier: Modifier = Modifier, onClick: (Medium) -> Unit ) { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/PopularSeasonOverview.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/PopularSeasonOverview.kt index 5a8544e..72c4ac4 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/PopularSeasonOverview.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/PopularSeasonOverview.kt @@ -25,12 +25,13 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @Composable fun PopularSeasonOverview( state: Flow, current: Boolean, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit, ) { val loadingState by state.collectAsStateWithLifecycle( @@ -76,7 +77,7 @@ private fun Loading() { private fun SuccessContent( data: List, current: Boolean, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit ) { val listState = rememberLazyListState( diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/TrendingOverview.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/TrendingOverview.kt index 8d28f74..3b15c84 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/TrendingOverview.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/TrendingOverview.kt @@ -23,11 +23,12 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @Composable fun TrendingOverview( state: Flow, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit, ) { val loadingState by state.collectAsStateWithLifecycle(StateSaver.Home.trendingState) @@ -65,7 +66,7 @@ private fun Loading() { @Composable private fun SuccessContent( data: List, - titleLanguage: AppSettings.TitleLanguage?, + titleLanguage: SettingsTitle?, onClick: (Medium) -> Unit ) { val listState = rememberLazyListState( 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 d136b18..0049e5a 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 @@ -1,17 +1,21 @@ package dev.datlag.aniflow.ui.navigation.screen.initial.settings import dev.datlag.aniflow.anilist.model.User -import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.aniflow.ui.navigation.Component import kotlinx.coroutines.flow.Flow +import dev.datlag.aniflow.settings.model.Color as SettingsColor +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar interface SettingsComponent : Component { val user: Flow val adultContent: Flow - val selectedColor: Flow - val selectedTitleLanguage: Flow + val selectedColor: Flow + val selectedTitleLanguage: Flow + val selectedCharLanguage: Flow fun changeAdultContent(value: Boolean) - fun changeProfileColor(value: AppSettings.Color?) - fun changeTitleLanguage(value: AppSettings.TitleLanguage?) + fun changeProfileColor(value: SettingsColor?) + fun changeTitleLanguage(value: SettingsTitle?) + fun changeCharLanguage(value: SettingsChar?) } \ 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 3babef5..8e4e1d6 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 @@ -41,12 +41,14 @@ import dev.datlag.aniflow.common.plus import dev.datlag.aniflow.common.toComposeColor import dev.datlag.aniflow.common.toComposeString import dev.datlag.aniflow.other.StateSaver -import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.tooling.compose.onClick import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import dev.icerock.moko.resources.compose.stringResource import io.github.aakira.napier.Napier import kotlinx.coroutines.flow.update +import dev.datlag.aniflow.settings.model.Color as SettingsColor +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalMaterial3Api::class) @Composable @@ -116,7 +118,7 @@ fun SettingsScreen(component: SettingsComponent) { StateSaver.updateTemporaryColor(null) } ) - val colors = remember { AppSettings.Color.all.toList() } + val colors = remember { SettingsColor.all.toList() } OptionDialog( state = useCase, @@ -175,7 +177,7 @@ fun SettingsScreen(component: SettingsComponent) { item { val selectedTitle by component.selectedTitleLanguage.collectAsStateWithLifecycle(null) val useCase = rememberUseCaseState() - val languages = remember { AppSettings.TitleLanguage.all.toList() } + val languages = remember { SettingsTitle.all.toList() } OptionDialog( state = useCase, @@ -218,6 +220,52 @@ fun SettingsScreen(component: SettingsComponent) { } } } + item { + val selectedChar by component.selectedCharLanguage.collectAsStateWithLifecycle(null) + val useCase = rememberUseCaseState() + val languages = remember { SettingsChar.all.toList() } + + OptionDialog( + state = useCase, + selection = OptionSelection.Single( + options = languages.map { + Option( + selected = it == selectedChar, + titleText = stringResource(it.toComposeString()) + ) + }, + onSelectOption = { option, _ -> + component.changeCharLanguage(languages[option]) + } + ), + config = OptionConfig( + mode = DisplayMode.LIST, + ) + ) + + Row( + modifier = Modifier.fillParentMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + imageVector = Icons.Filled.PersonPin, + contentDescription = null + ) + Text( + text = "Character 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) @@ -257,19 +305,4 @@ fun SettingsScreen(component: SettingsComponent) { StateSaver.List.settingsOverviewOffset = listState.firstVisibleItemScrollOffset } } -} - -@OptIn(ExperimentalStdlibApi::class) -@Composable -fun ColorItem( - color: AppSettings.Color, - onClick: (AppSettings.Color) -> Unit -) { - Card( - modifier = Modifier.size(48.dp), - onClick = { onClick(color) }, - colors = CardDefaults.cardColors(containerColor = color.toComposeColor()), - shape = CircleShape, - content = { } - ) } \ No newline at end of file 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 382d49a..460b06a 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 @@ -6,12 +6,14 @@ import dev.datlag.aniflow.anilist.model.User import dev.datlag.aniflow.common.onRender import dev.datlag.aniflow.other.UserHelper import dev.datlag.aniflow.settings.Settings -import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.tooling.compose.ioDispatcher import dev.datlag.tooling.decompose.ioScope import kotlinx.coroutines.flow.* import org.kodein.di.DI import org.kodein.di.instance +import dev.datlag.aniflow.settings.model.Color as SettingsColor +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar class SettingsScreenComponent( componentContext: ComponentContext, @@ -23,8 +25,9 @@ class SettingsScreenComponent( override val user: Flow = userHelper.user override val adultContent: Flow = appSettings.adultContent - override val selectedColor: Flow = appSettings.color - override val selectedTitleLanguage: Flow = appSettings.titleLanguage + override val selectedColor: Flow = appSettings.color + override val selectedTitleLanguage: Flow = appSettings.titleLanguage + override val selectedCharLanguage: Flow = appSettings.charLanguage @Composable override fun render() { @@ -34,20 +37,26 @@ class SettingsScreenComponent( } override fun changeAdultContent(value: Boolean) { - launchDefault { + launchIO { userHelper.updateAdultSetting(value) } } - override fun changeProfileColor(value: AppSettings.Color?) { - launchDefault { + override fun changeProfileColor(value: SettingsColor?) { + launchIO { userHelper.updateProfileColorSetting(value) } } - override fun changeTitleLanguage(value: AppSettings.TitleLanguage?) { - launchDefault { + override fun changeTitleLanguage(value: SettingsTitle?) { + launchIO { userHelper.updateTitleLanguage(value) } } + + override fun changeCharLanguage(value: SettingsChar?) { + launchIO { + userHelper.updateCharLanguage(value) + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumComponent.kt index 34a46d3..3040140 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumComponent.kt @@ -13,10 +13,13 @@ import dev.datlag.aniflow.ui.navigation.ContentHolderComponent import dev.datlag.aniflow.ui.navigation.DialogComponent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar interface MediumComponent : ContentHolderComponent { val initialMedium: Medium - val titleLanguage: Flow + val titleLanguage: Flow + val charLanguage: Flow val mediumState: StateFlow val isAdult: Flow diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt index eea28da..86705a5 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt @@ -116,6 +116,7 @@ fun MediumScreen(component: MediumComponent) { if (!notReleased) { val uriHandler = LocalUriHandler.current + val userHelper by LocalDI.current.instance() EditFAB( displayAdd = !alreadyAdded, @@ -125,7 +126,7 @@ fun MediumScreen(component: MediumComponent) { }, onRate = { - + uriHandler.openUri(userHelper.loginUrl) }, onProgress = { // ratingState.show() @@ -184,6 +185,7 @@ fun MediumScreen(component: MediumComponent) { CharacterSection( initialMedium = component.initialMedium, characterFlow = component.characters, + charLanguage = component.charLanguage, modifier = Modifier.fillParentMaxWidth().animateItemPlacement() ) { char -> component.showCharacter(char) diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreenComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreenComponent.kt index 6e570bd..0b048c6 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreenComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreenComponent.kt @@ -25,6 +25,7 @@ import dev.datlag.aniflow.other.Constants import dev.datlag.aniflow.other.UserHelper import dev.datlag.aniflow.settings.Settings import dev.datlag.aniflow.settings.model.AppSettings +import dev.datlag.aniflow.settings.model.CharLanguage import dev.datlag.aniflow.ui.navigation.DialogComponent import dev.datlag.aniflow.ui.navigation.screen.medium.dialog.character.CharacterDialogComponent import dev.datlag.tooling.alsoTrue @@ -39,6 +40,7 @@ import kotlinx.datetime.Instant import org.kodein.di.DI import org.kodein.di.instance import kotlin.time.Duration.Companion.seconds +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle class MediumScreenComponent( componentContext: ComponentContext, @@ -52,7 +54,8 @@ class MediumScreenComponent( private val appSettings by di.instance() private val userHelper by di.instance() - override val titleLanguage: Flow = appSettings.titleLanguage + override val titleLanguage: Flow = appSettings.titleLanguage + override val charLanguage: Flow = appSettings.charLanguage private val mediumStateMachine = MediumStateMachine( client = aniListClient, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterCard.kt index c20e36c..81d7f52 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterCard.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterCard.kt @@ -21,11 +21,15 @@ import coil3.compose.rememberAsyncImagePainter import dev.datlag.aniflow.anilist.model.Character import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.common.preferredName +import dev.datlag.aniflow.settings.model.CharLanguage import dev.datlag.tooling.compose.ifTrue +import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle +import kotlinx.coroutines.flow.Flow @Composable fun CharacterCard( char: Character, + languageFlow: Flow, modifier: Modifier = Modifier, onClick: (Character) -> Unit ) { @@ -67,8 +71,10 @@ fun CharacterCard( modifier = Modifier.weight(1F), contentAlignment = Alignment.Center ) { + val charLanguage by languageFlow.collectAsStateWithLifecycle(null) + Text( - text = char.preferredName(), + text = char.preferredName(charLanguage), style = MaterialTheme.typography.labelLarge, maxLines = 2, softWrap = true, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterSection.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterSection.kt index a2e782d..18c5d7f 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterSection.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CharacterSection.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp import dev.datlag.aniflow.SharedRes import dev.datlag.aniflow.anilist.model.Character import dev.datlag.aniflow.anilist.model.Medium +import dev.datlag.aniflow.settings.model.CharLanguage import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.flow.Flow @@ -22,6 +23,7 @@ import kotlinx.coroutines.flow.StateFlow fun CharacterSection( initialMedium: Medium, characterFlow: Flow>, + charLanguage: Flow, modifier: Modifier = Modifier, onClick: (Character) -> Unit ) { @@ -46,6 +48,7 @@ fun CharacterSection( items(characters.toList()) { char -> CharacterCard( char = char, + languageFlow = charLanguage, modifier = Modifier.width(96.dp).height(200.dp), onClick = onClick ) diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt index c5aab86..2efe627 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt @@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlin.math.max import kotlin.math.min +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class) @Composable @@ -49,7 +50,7 @@ fun CollapsingToolbar( state: TopAppBarState, scrollBehavior: TopAppBarScrollBehavior, initialMedium: Medium, - titleLanguageFlow: Flow, + titleLanguageFlow: Flow, mediumStateFlow: StateFlow, bannerImageFlow: Flow, coverImage: Medium.CoverImage, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterComponent.kt index c203126..0f26149 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterComponent.kt @@ -2,6 +2,7 @@ package dev.datlag.aniflow.ui.navigation.screen.medium.dialog.character import dev.datlag.aniflow.anilist.CharacterStateMachine import dev.datlag.aniflow.anilist.model.Character +import dev.datlag.aniflow.settings.model.CharLanguage import dev.datlag.aniflow.ui.navigation.DialogComponent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -10,6 +11,7 @@ interface CharacterComponent : DialogComponent { val initialChar: Character val state: StateFlow + val charLanguage: Flow val image: Flow val name: Flow diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt index b8c1d30..1d905a2 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt @@ -53,6 +53,7 @@ fun CharacterDialog(component: CharacterComponent) { sheetState = sheetState ) { val name by component.name.collectAsStateWithLifecycle(component.initialChar.name) + val charLanguage by component.charLanguage.collectAsStateWithLifecycle(null) Box( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), @@ -89,7 +90,7 @@ fun CharacterDialog(component: CharacterComponent) { ), contentScale = ContentScale.Crop, alignment = Alignment.Center, - contentDescription = component.initialChar.preferredName() + contentDescription = component.initialChar.preferredName(charLanguage) ) this@ModalBottomSheet.AnimatedVisibility( @@ -133,7 +134,7 @@ fun CharacterDialog(component: CharacterComponent) { Text( modifier = Modifier.align(Alignment.CenterHorizontally), - text = name.preferred(), + text = name.preferred(charLanguage), style = MaterialTheme.typography.headlineMedium, maxLines = 2, overflow = TextOverflow.Ellipsis, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialogComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialogComponent.kt index 5754be9..e6f1db7 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialogComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialogComponent.kt @@ -11,6 +11,9 @@ import dev.datlag.aniflow.common.nullableFirebaseInstance import dev.datlag.aniflow.common.onRender import dev.datlag.aniflow.model.safeFirstOrNull import dev.datlag.aniflow.other.Constants +import dev.datlag.aniflow.settings.Settings +import dev.datlag.aniflow.settings.model.CharLanguage +import dev.datlag.aniflow.settings.model.TitleLanguage import dev.datlag.tooling.compose.ioDispatcher import dev.datlag.tooling.decompose.ioScope import dev.datlag.tooling.safeCast @@ -34,6 +37,9 @@ class CharacterDialogComponent( id = initialChar.id ) + private val appSettings by di.instance() + override val charLanguage: Flow = appSettings.charLanguage + override val state = characterStateMachine.state.flowOn( context = ioDispatcher() ).stateIn( diff --git a/composeApp/src/commonMain/moko-resources/base/strings.xml b/composeApp/src/commonMain/moko-resources/base/strings.xml index 777344a..d329de8 100644 --- a/composeApp/src/commonMain/moko-resources/base/strings.xml +++ b/composeApp/src/commonMain/moko-resources/base/strings.xml @@ -51,4 +51,7 @@ Romaji English Native + Romaji (Western Order) + Romaji + 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 4247f3a..d841bab 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,21 +3,22 @@ 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 { +import dev.datlag.aniflow.settings.model.TitleLanguage as SettingsTitle +import dev.datlag.aniflow.settings.model.CharLanguage as SettingsChar +import dev.datlag.aniflow.model.appendWithSpace +actual fun Medium.Title.preferred(setting: SettingsTitle?): String { if (setting != null) { return when (setting) { - is AppSettings.TitleLanguage.Romaji -> this.romaji?.ifBlank { null } + is SettingsTitle.Romaji -> this.romaji?.ifBlank { null } ?: this.english?.ifBlank { null } ?: this.native?.ifBlank { null } ?: "" - is AppSettings.TitleLanguage.English -> this.english?.ifBlank { null } + is SettingsTitle.English -> this.english?.ifBlank { null } ?: this.romaji?.ifBlank { null } ?: this.native?.ifBlank { null } ?: "" - is AppSettings.TitleLanguage.Native -> this.native?.ifBlank { null } + is SettingsTitle.Native -> this.native?.ifBlank { null } ?: this.romaji?.ifBlank { null } ?: this.english?.ifBlank { null } ?: "" @@ -31,16 +32,48 @@ actual fun Medium.Title.preferred(setting: AppSettings.TitleLanguage?): String { ?: "" } -actual fun Character.Name.preferred(): String { +actual fun Character.Name.preferred(setting: SettingsChar?): String { + if (setting != null) { + return when (setting) { + is SettingsChar.RomajiWestern -> buildString { + appendWithSpace(this@preferred.first) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.last) + }.trim().ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: this.full?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + + is SettingsChar.Romaji -> buildString { + appendWithSpace(this@preferred.last) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.first) + }.trim().ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: this.full?.ifBlank { null } + ?: this.native?.ifBlank { null } + ?: "" + + is SettingsChar.Native -> this.native?.ifBlank { null } + ?: this.userPreferred?.ifBlank { null } + ?: buildString { + appendWithSpace(this@preferred.last) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.first) + }.trim().ifBlank { null } + ?: this.full?.ifBlank { null } + ?: "" + } + } + return this.userPreferred?.ifBlank { null } ?: this.full?.ifBlank { null } ?: buildString { - append(this@preferred.first) - append(" ") - append(this@preferred.middle) - append(" ") - append(this@preferred.last) - }.ifBlank { null } + appendWithSpace(this@preferred.first) + appendWithSpace(this@preferred.middle) + appendWithSpace(this@preferred.last) + }.trim().ifBlank { null } ?: this.native?.ifBlank { null } ?: "" } \ No newline at end of file diff --git a/model/src/commonMain/kotlin/dev/datlag/aniflow/model/ExtendAny.kt b/model/src/commonMain/kotlin/dev/datlag/aniflow/model/ExtendAny.kt index 332c124..64ebe93 100644 --- a/model/src/commonMain/kotlin/dev/datlag/aniflow/model/ExtendAny.kt +++ b/model/src/commonMain/kotlin/dev/datlag/aniflow/model/ExtendAny.kt @@ -8,4 +8,13 @@ inline fun T?.ifValueOrNull(value: T, defaultValue: () -> T): T { return if (this == value || this == null) defaultValue() else this } -fun Boolean.toInt() = if (this) 1 else 0 \ No newline at end of file +fun Boolean.toInt() = if (this) 1 else 0 + +fun StringBuilder.appendWithSpace(str: String?): StringBuilder { + var builder = this + if (!str.isNullOrBlank()) { + builder = builder.append(str) + builder = builder.append(" ") + } + return builder +} \ No newline at end of file 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 0a5ba9d..32f8504 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/AppSettingsSerializer.kt @@ -13,7 +13,8 @@ import okio.BufferedSource data object AppSettingsSerializer : OkioSerializer { override val defaultValue: AppSettings = AppSettings( color = null, - titleLanguage = null + titleLanguage = null, + charLanguage = 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 d0786cd..556e15a 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/DataStoreAppSettings.kt @@ -2,6 +2,9 @@ package dev.datlag.aniflow.settings import androidx.datastore.core.DataStore import dev.datlag.aniflow.settings.model.AppSettings +import dev.datlag.aniflow.settings.model.CharLanguage +import dev.datlag.aniflow.settings.model.Color +import dev.datlag.aniflow.settings.model.TitleLanguage import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -10,8 +13,9 @@ class DataStoreAppSettings( private val dateStore: DataStore ) : Settings.PlatformAppSettings { override val adultContent: Flow = dateStore.data.map { it.adultContent }.distinctUntilChanged() - override val color: Flow = dateStore.data.map { it.color }.distinctUntilChanged() - override val titleLanguage: Flow = dateStore.data.map { it.titleLanguage }.distinctUntilChanged() + override val color: Flow = dateStore.data.map { it.color }.distinctUntilChanged() + override val titleLanguage: Flow = dateStore.data.map { it.titleLanguage }.distinctUntilChanged() + override val charLanguage: Flow = dateStore.data.map { it.charLanguage }.distinctUntilChanged() override suspend fun setAdultContent(value: Boolean) { dateStore.updateData { @@ -21,7 +25,7 @@ class DataStoreAppSettings( } } - override suspend fun setColor(value: AppSettings.Color?) { + override suspend fun setColor(value: Color?) { dateStore.updateData { it.copy( color = value @@ -29,7 +33,7 @@ class DataStoreAppSettings( } } - override suspend fun setTitleLanguage(value: AppSettings.TitleLanguage?) { + override suspend fun setTitleLanguage(value: TitleLanguage?) { dateStore.updateData { it.copy( titleLanguage = value @@ -37,16 +41,26 @@ class DataStoreAppSettings( } } + override suspend fun setCharLanguage(value: CharLanguage?) { + dateStore.updateData { + it.copy( + charLanguage = value + ) + } + } + override suspend fun setData( adultContent: Boolean, - color: AppSettings.Color?, - titleLanguage: AppSettings.TitleLanguage?, + color: Color?, + titleLanguage: TitleLanguage?, + charLanguage: CharLanguage?, ) { dateStore.updateData { it.copy( adultContent = adultContent, color = color, titleLanguage = titleLanguage, + charLanguage = charLanguage, ) } } 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 70038f1..694bd8d 100644 --- a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/Settings.kt @@ -1,7 +1,6 @@ package dev.datlag.aniflow.settings -import dev.datlag.aniflow.settings.model.AppSettings -import dev.datlag.aniflow.settings.model.UserSettings +import dev.datlag.aniflow.settings.model.* import kotlinx.coroutines.flow.Flow data object Settings { @@ -19,19 +18,22 @@ data object Settings { interface PlatformAppSettings { val adultContent: Flow - val color: Flow - val titleLanguage: Flow + val color: Flow + val titleLanguage: Flow + val charLanguage: Flow suspend fun setAdultContent(value: Boolean) - suspend fun setColor(value: AppSettings.Color?) + suspend fun setColor(value: Color?) suspend fun setColor(value: String?) = setColor(value?.let { - AppSettings.Color.fromString(it) + Color.fromString(it) }) - suspend fun setTitleLanguage(value: AppSettings.TitleLanguage?) + suspend fun setTitleLanguage(value: TitleLanguage?) + suspend fun setCharLanguage(value: CharLanguage?) suspend fun setData( adultContent: Boolean, - color: AppSettings.Color?, - titleLanguage: AppSettings.TitleLanguage?, + color: Color?, + titleLanguage: TitleLanguage?, + charLanguage: CharLanguage?, ) } } \ 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 b5353de..e2a9b65 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 @@ -1,13 +1,7 @@ package dev.datlag.aniflow.settings.model import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.protobuf.ProtoNumber @Serializable @@ -16,162 +10,5 @@ data class AppSettings( @ProtoNumber(1) val adultContent: Boolean = false, @ProtoNumber(2) val color: Color?, @ProtoNumber(3) val titleLanguage: TitleLanguage?, -) { - - @Serializable(with = Color.ColorSerializer::class) - sealed interface Color { - val hex: String - val label: String - get() = hex - - @Serializable - data object Blue : Color { - override val hex: String = "#3db4f2" - override val label: String = "blue" - } - - @Serializable - data object Purple : Color { - override val hex: String = "#c063ff" - override val label: String = "purple" - } - - @Serializable - data object Pink : Color { - override val hex: String = "#fc9dd6" - override val label: String = "pink" - } - - @Serializable - data object Orange : Color { - override val hex: String = "#ef881a" - override val label: String = "orange" - } - - @Serializable - data object Red : Color { - override val hex: String = "#e13333" - override val label: String = "red" - } - - @Serializable - data object Green : Color { - override val hex: String = "#4cca51" - override val label: String = "green" - } - - @Serializable - data object Gray : Color { - override val hex: String = "#677b94" - override val label: String = "gray" - } - - @Serializable - data class Custom( - override val hex: String - ) : Color - - companion object ColorSerializer : KSerializer { - val all: Set = setOf( - Blue, - Purple, - Pink, - Orange, - Red, - Green, - Gray - ) - - fun fromString(value: String?): Color? = when { - value == null -> null - value.equals(Blue.label, ignoreCase = true) -> Blue - value.equals(Purple.label, ignoreCase = true) -> Purple - value.equals(Pink.label, ignoreCase = true) -> Pink - value.equals(Orange.label, ignoreCase = true) -> Orange - value.equals(Red.label, ignoreCase = true) -> Red - value.equals(Green.label, ignoreCase = true) -> Green - value.equals(Gray.label, ignoreCase = true) || value.equals("grey", ignoreCase = true) -> Gray - else -> { - if (value.startsWith("#")) { - Custom(value) - } else { - null - } - } - } - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING) - - override fun deserialize(decoder: Decoder): Color? { - return if (decoder.decodeNotNullMark()) { - fromString(decoder.decodeString()) - } else { - decoder.decodeNull() - } - } - - override fun serialize(encoder: Encoder, value: Color?) { - if (value != null) { - encoder.encodeNotNullMark() - encoder.encodeString(value.label) - } else { - encoder.encodeNull() - } - } - } - } - - @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() - } - } - } - } -} + @ProtoNumber(4) val charLanguage: CharLanguage? +) diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/CharLanguage.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/CharLanguage.kt new file mode 100644 index 0000000..0ceb92d --- /dev/null +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/CharLanguage.kt @@ -0,0 +1,66 @@ +package dev.datlag.aniflow.settings.model + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable(with = CharLanguage.CharSerializer::class) +sealed interface CharLanguage { + val id: Int + + @Serializable + data object RomajiWestern : CharLanguage { + override val id: Int = 1 + } + + @Serializable + data object Romaji : CharLanguage { + override val id: Int = 2 + } + + @Serializable + data object Native : CharLanguage { + override val id: Int = 3 + } + + companion object CharSerializer : KSerializer { + val all: Set = setOf( + RomajiWestern, + Romaji, + Native + ) + + internal fun fromId(value: Int): CharLanguage? = when (value) { + 1 -> RomajiWestern + 2 -> Romaji + 3 -> Native + else -> null + } + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CharLanguage", PrimitiveKind.INT) + + @OptIn(ExperimentalSerializationApi::class) + override fun deserialize(decoder: Decoder): CharLanguage? { + return if (decoder.decodeNotNullMark()) { + fromId(decoder.decodeInt()) + } else { + decoder.decodeNull() + } + } + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: CharLanguage?) { + if (value != null) { + encoder.encodeNotNullMark() + encoder.encodeInt(value.id) + } else { + encoder.encodeNull() + } + } + } +} \ No newline at end of file diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/Color.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/Color.kt new file mode 100644 index 0000000..80cb784 --- /dev/null +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/Color.kt @@ -0,0 +1,115 @@ +package dev.datlag.aniflow.settings.model + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable(with = Color.ColorSerializer::class) +sealed interface Color { + val hex: String + val label: String + get() = hex + + @Serializable + data object Blue : Color { + override val hex: String = "#3db4f2" + override val label: String = "blue" + } + + @Serializable + data object Purple : Color { + override val hex: String = "#c063ff" + override val label: String = "purple" + } + + @Serializable + data object Pink : Color { + override val hex: String = "#fc9dd6" + override val label: String = "pink" + } + + @Serializable + data object Orange : Color { + override val hex: String = "#ef881a" + override val label: String = "orange" + } + + @Serializable + data object Red : Color { + override val hex: String = "#e13333" + override val label: String = "red" + } + + @Serializable + data object Green : Color { + override val hex: String = "#4cca51" + override val label: String = "green" + } + + @Serializable + data object Gray : Color { + override val hex: String = "#677b94" + override val label: String = "gray" + } + + @Serializable + data class Custom( + override val hex: String + ) : Color + + companion object ColorSerializer : KSerializer { + val all: Set = setOf( + Blue, + Purple, + Pink, + Orange, + Red, + Green, + Gray + ) + + fun fromString(value: String?): Color? = when { + value == null -> null + value.equals(Blue.label, ignoreCase = true) -> Blue + value.equals(Purple.label, ignoreCase = true) -> Purple + value.equals(Pink.label, ignoreCase = true) -> Pink + value.equals(Orange.label, ignoreCase = true) -> Orange + value.equals(Red.label, ignoreCase = true) -> Red + value.equals(Green.label, ignoreCase = true) -> Green + value.equals(Gray.label, ignoreCase = true) || value.equals("grey", ignoreCase = true) -> Gray + else -> { + if (value.startsWith("#")) { + Custom(value) + } else { + null + } + } + } + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING) + + @OptIn(ExperimentalSerializationApi::class) + override fun deserialize(decoder: Decoder): Color? { + return if (decoder.decodeNotNullMark()) { + fromString(decoder.decodeString()) + } else { + decoder.decodeNull() + } + } + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: Color?) { + if (value != null) { + encoder.encodeNotNullMark() + encoder.encodeString(value.label) + } else { + encoder.encodeNull() + } + } + } +} \ No newline at end of file diff --git a/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/TitleLanguage.kt b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/TitleLanguage.kt new file mode 100644 index 0000000..65b9aab --- /dev/null +++ b/settings/src/commonMain/kotlin/dev/datlag/aniflow/settings/model/TitleLanguage.kt @@ -0,0 +1,66 @@ +package dev.datlag.aniflow.settings.model + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@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("TitleLanguage", PrimitiveKind.INT) + + @OptIn(ExperimentalSerializationApi::class) + override fun deserialize(decoder: Decoder): TitleLanguage? { + return if (decoder.decodeNotNullMark()) { + fromId(decoder.decodeInt()) + } else { + decoder.decodeNull() + } + } + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: TitleLanguage?) { + if (value != null) { + encoder.encodeNotNullMark() + encoder.encodeInt(value.id) + } else { + encoder.encodeNull() + } + } + } +} \ No newline at end of file