From 719a0720b8da948933c15418a395eaaa458801ee Mon Sep 17 00:00:00 2001 From: Syer10 Date: Thu, 17 Nov 2022 19:49:27 -0500 Subject: [PATCH] Start adding iOS support to presentation --- .../ca/gosyer/jui/core/io/OkioExtensions.kt | 6 ++ presentation/build.gradle.kts | 28 ++++++- .../licenses/components/AndroidLicenses.kt | 5 ++ .../sources/components/AndroidSourcesMenu.kt | 6 +- .../jui/ui/base/state/SavedStateHandleFlow.kt | 8 +- .../jui/ui/categories/CategoriesScreen.kt | 1 + .../jui/ui/library/LibraryScreenViewModel.kt | 4 +- .../licenses/components/LicensesContent.kt | 17 ++++- .../ui/reader/loader/TachideskPageLoader.kt | 3 + .../jui/ui/settings/SettingsBackupScreen.kt | 2 + .../globalsearch/GlobalSearchViewModel.kt | 1 + .../ca/gosyer/jui/ui/util/lang/Collator.kt | 6 +- .../kotlin/ca/gosyer/jui/ui/util/lang/Okio.kt | 2 +- .../licenses/components/DesktopLicenses.kt | 5 ++ .../ca/gosyer/jui/ui/IosViewModelComponent.kt | 28 +++++++ .../jui/ui/base/components/IosTooltipArea.kt | 41 ++++++++++ .../gosyer/jui/ui/base/file/IosFileChooser.kt | 22 ++++++ .../gosyer/jui/ui/base/file/IosFileSaver.kt | 30 ++++++++ .../ui/base/image/IosBitmapDecoderFactory.kt | 14 ++++ .../ui/base/image/IosImageLoaderBuilder.kt | 44 +++++++++++ .../jui/ui/base/navigation/ActionIcon.kt | 20 +++++ .../jui/ui/base/navigation/IosBackHandler.kt | 12 +++ .../jui/ui/base/prefs/IosColorExtensions.kt | 37 ++++++++++ .../gosyer/jui/ui/base/screen/BaseScreen.kt | 16 ++++ .../jui/ui/base/state/IosSavedStateHandle.kt | 50 +++++++++++++ .../jui/ui/base/theme/ThemeScrollbarStyle.kt | 21 ++++++ .../jui/ui/categories/OpenCategories.kt | 29 ++++++++ .../jui/ui/downloads/IosDownloadService.kt | 19 +++++ .../ui/library/components/IosLibraryGrid.kt | 51 +++++++++++++ .../ui/main/about/components/getDebugInfo.kt | 22 ++++++ .../about/licenses/components/IosLicenses.kt | 74 +++++++++++++++++++ .../main/components/DebugOverlayViewModel.kt | 18 +++++ .../components/IosLibraryUpdatesService.kt | 19 +++++ .../jui/ui/manga/components/IosChapterItem.kt | 24 ++++++ .../ca/gosyer/jui/ui/reader/IosReaderMenu.kt | 50 +++++++++++++ .../settings/IosSettingsAppearenceScreen.kt | 9 +++ .../ui/settings/IosSettingsServerScreen.kt | 20 +++++ .../ui/sources/components/IosSourcesMenu.kt | 17 +++++ .../ui/updates/components/IosUpdatesItem.kt | 23 ++++++ .../ca/gosyer/jui/ui/util/compose/Image.kt | 23 ++++++ .../ca/gosyer/jui/ui/util/compose/IosColor.kt | 18 +++++ .../ca/gosyer/jui/ui/util/lang/Collator.kt | 21 ++++++ .../ca/gosyer/jui/ui/util/lang/IosOkio.kt | 16 ++++ .../gosyer/jui/ui/util/lang/StringFormat.kt | 70 ++++++++++++++++++ .../ca/gosyer/jui/ui/util/lang/Collator.kt | 16 ++-- .../ca/gosyer/jui/ui/util/lang/JvmOkio.kt | 2 +- .../jui/uicore/resources/IosStringResource.kt | 16 +--- .../jui/uicore/resources/IosStringResource.kt | 10 ++- .../ca/gosyer/jui/uicore/vm/ContextWrapper.kt | 13 +++- .../jui/uicore/resources/IosStringResource.kt | 16 +--- .../jui/uicore/resources/IosStringResource.kt | 16 +--- 51 files changed, 977 insertions(+), 64 deletions(-) create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/IosViewModelComponent.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/components/IosTooltipArea.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileChooser.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileSaver.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosBitmapDecoderFactory.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/ActionIcon.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/IosBackHandler.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/screen/BaseScreen.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/state/IosSavedStateHandle.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/theme/ThemeScrollbarStyle.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/categories/OpenCategories.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/downloads/IosDownloadService.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/library/components/IosLibraryGrid.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/components/getDebugInfo.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/IosLicenses.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/DebugOverlayViewModel.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/IosLibraryUpdatesService.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/manga/components/IosChapterItem.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsAppearenceScreen.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsServerScreen.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/sources/components/IosSourcesMenu.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/updates/components/IosUpdatesItem.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/Image.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/IosColor.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/IosOkio.kt create mode 100644 presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/StringFormat.kt diff --git a/core/src/commonMain/kotlin/ca/gosyer/jui/core/io/OkioExtensions.kt b/core/src/commonMain/kotlin/ca/gosyer/jui/core/io/OkioExtensions.kt index db62bae1..8298f3d5 100644 --- a/core/src/commonMain/kotlin/ca/gosyer/jui/core/io/OkioExtensions.kt +++ b/core/src/commonMain/kotlin/ca/gosyer/jui/core/io/OkioExtensions.kt @@ -7,7 +7,9 @@ package ca.gosyer.jui.core.io import ca.gosyer.jui.core.lang.withIOContext +import okio.Buffer import okio.BufferedSink +import okio.BufferedSource import okio.FileSystem import okio.Path import okio.Source @@ -32,3 +34,7 @@ suspend fun Source.copyTo(sink: BufferedSink) { } } } + +fun ByteArray.source(): BufferedSource { + return Buffer().write(this) +} \ No newline at end of file diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index eacc3f9e..fa47490d 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -25,6 +25,9 @@ kotlin { } } } + iosX64() + iosArm64() + iosSimulatorArm64() sourceSets { all { @@ -53,13 +56,16 @@ kotlin { api(libs.dateTime) api(libs.immutableCollections) api(libs.aboutLibraries.core) - api(libs.aboutLibraries.ui) + api(projects.core) api(projects.i18n) api(projects.domain) api(projects.data) api(projects.uiCore) - api(compose.desktop.currentOs) + api(compose.runtime) + api(compose.foundation) + api(compose.ui) + api(compose.material) api(compose("org.jetbrains.compose.ui:ui-util")) api(compose.materialIconsExtended) } @@ -75,6 +81,8 @@ kotlin { dependsOn(commonMain) dependencies { api(kotlin("stdlib-jdk8")) + api(libs.aboutLibraries.ui) + api(compose.desktop.currentOs) } } val jvmTest by creating { @@ -107,6 +115,22 @@ kotlin { val androidTest by getting { dependsOn(jvmTest) } + + val iosMain by creating { + dependsOn(commonMain) + } + val iosTest by creating { + dependsOn(commonTest) + } + + listOf( + "iosX64", + "iosArm64", + "iosSimulatorArm64", + ).forEach { + getByName(it + "Main").dependsOn(iosMain) + getByName(it + "Test").dependsOn(iosTest) + } } } diff --git a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/AndroidLicenses.kt b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/AndroidLicenses.kt index d6d5e790..85b2c196 100644 --- a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/AndroidLicenses.kt +++ b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/AndroidLicenses.kt @@ -18,6 +18,7 @@ import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.ui.compose.Libraries import com.mikepenz.aboutlibraries.ui.compose.LibraryColors +import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults import com.mikepenz.aboutlibraries.util.withContext import kotlinx.collections.immutable.ImmutableList @@ -35,6 +36,10 @@ actual fun getLicenses(): Libs? { return libs } +actual typealias LibraryDefaults = LibraryDefaults + +actual typealias LibraryColors = LibraryColors + @Composable actual fun InternalAboutLibraries( libraries: ImmutableList, diff --git a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/sources/components/AndroidSourcesMenu.kt b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/sources/components/AndroidSourcesMenu.kt index 3ed2cc2e..60cacfaf 100644 --- a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/sources/components/AndroidSourcesMenu.kt +++ b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/sources/components/AndroidSourcesMenu.kt @@ -12,8 +12,6 @@ import androidx.compose.ui.Modifier actual fun Modifier.sourceSideMenuItem( onSourceTabClick: () -> Unit, onSourceCloseTabClick: () -> Unit -): Modifier = Modifier.clickable( - onClick = { - onSourceTabClick() - } +): Modifier = clickable( + onClick = onSourceTabClick ) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/state/SavedStateHandleFlow.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/state/SavedStateHandleFlow.kt index 9abade44..cfc71e6a 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/state/SavedStateHandleFlow.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/state/SavedStateHandleFlow.kt @@ -7,7 +7,10 @@ package ca.gosyer.jui.ui.base.state import ca.gosyer.jui.uicore.vm.ViewModel +import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.internal.SynchronizedObject +import kotlinx.coroutines.internal.synchronized import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -17,14 +20,17 @@ fun SavedStateHandle.getStateFlow( return SavedStateHandleDelegate(this, initialValue) } +@OptIn(InternalCoroutinesApi::class) class SavedStateHandleDelegate( private val savedStateHandle: SavedStateHandle, private val initialValue: () -> T ) : ReadOnlyProperty> { + private val synchronizedObject = SynchronizedObject() + private var item: SavedStateHandleStateFlow? = null override fun getValue(thisRef: ViewModel, property: KProperty<*>): SavedStateHandleStateFlow { - return item ?: synchronized(this) { + return item ?: synchronized(synchronizedObject) { if (item == null) { savedStateHandle.getSavedStateFlow(property.name, initialValue) .also { item = it } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/categories/CategoriesScreen.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/categories/CategoriesScreen.kt index 3442b60a..b1a43c44 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/categories/CategoriesScreen.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/categories/CategoriesScreen.kt @@ -13,6 +13,7 @@ import ca.gosyer.jui.ui.viewModel import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.ScreenKey import cafe.adriel.voyager.core.screen.uniqueScreenKey +import kotlin.jvm.Transient expect class CategoriesLauncher { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/library/LibraryScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/library/LibraryScreenViewModel.kt index 73bcece3..73525ce4 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/library/LibraryScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/library/LibraryScreenViewModel.kt @@ -25,7 +25,7 @@ import ca.gosyer.jui.domain.updates.interactor.UpdateLibrary import ca.gosyer.jui.i18n.MR import ca.gosyer.jui.ui.base.state.SavedStateHandle import ca.gosyer.jui.ui.base.state.getStateFlow -import ca.gosyer.jui.ui.util.lang.Collator +import ca.gosyer.jui.ui.util.lang.CollatorComparator import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.ViewModel import kotlinx.collections.immutable.ImmutableList @@ -191,7 +191,7 @@ class LibraryScreenViewModel @Inject constructor( val sortFn: (Manga, Manga) -> Int = when (sortMode) { Sort.ALPHABETICAL -> { val locale = Locale.current - val collator = Collator(locale); + val collator = CollatorComparator(locale); { a, b -> collator.compare(a.title.toLowerCase(locale), b.title.toLowerCase(locale)) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/LicensesContent.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/LicensesContent.kt index b812b8be..a19043dc 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/LicensesContent.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/LicensesContent.kt @@ -19,11 +19,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold +import androidx.compose.material.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.unit.dp import ca.gosyer.jui.i18n.MR @@ -38,8 +41,6 @@ import ca.gosyer.jui.uicore.insets.statusBars import ca.gosyer.jui.uicore.resources.stringResource import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library -import com.mikepenz.aboutlibraries.ui.compose.LibraryColors -import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -60,6 +61,18 @@ internal expect fun InternalAboutLibraries( onLibraryClick: ((Library) -> Unit)? ) +expect interface LibraryColors +expect object LibraryDefaults { + @Composable + fun libraryColors( + backgroundColor: Color = MaterialTheme.colors.background, + contentColor: Color = contentColorFor(backgroundColor), + badgeBackgroundColor: Color = MaterialTheme.colors.primary, + badgeContentColor: Color = contentColorFor(badgeBackgroundColor), + ): LibraryColors + val ContentPadding: PaddingValues +} + @Composable fun AboutLibraries( libraries: ImmutableList, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt index 8d674962..a3f66e0d 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt @@ -6,6 +6,8 @@ package ca.gosyer.jui.ui.reader.loader +import ca.gosyer.jui.core.io.SYSTEM +import ca.gosyer.jui.core.lang.IO import ca.gosyer.jui.core.lang.PriorityChannel import ca.gosyer.jui.core.lang.throwIfCancellation import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage @@ -39,6 +41,7 @@ import kotlinx.coroutines.launch import okio.BufferedSource import okio.FileSystem import okio.buffer +import okio.use import org.lighthousegames.logging.logging class TachideskPageLoader( diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt index a520171d..c8d93857 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt @@ -36,8 +36,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import ca.gosyer.jui.core.io.SYSTEM import ca.gosyer.jui.core.io.copyTo import ca.gosyer.jui.core.io.saveTo +import ca.gosyer.jui.core.lang.IO import ca.gosyer.jui.core.lang.throwIfCancellation import ca.gosyer.jui.domain.backup.interactor.ExportBackupFile import ca.gosyer.jui.domain.backup.interactor.ImportBackupFile diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt index d7a26c3d..6e36857b 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt @@ -7,6 +7,7 @@ package ca.gosyer.jui.ui.sources.globalsearch import androidx.compose.runtime.snapshots.SnapshotStateMap +import ca.gosyer.jui.core.lang.IO import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.source.interactor.GetSearchManga import ca.gosyer.jui.domain.source.interactor.GetSourceList diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt index 85c13a95..6c3d9b16 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt @@ -8,8 +8,6 @@ package ca.gosyer.jui.ui.util.lang import androidx.compose.ui.text.intl.Locale -expect fun Collator(locale: Locale): Collator - -expect class Collator { - fun compare(source: String, target: String): Int +expect class CollatorComparator(locale: Locale) : Comparator { + override fun compare(source: String, target: String): Int } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Okio.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Okio.kt index 78b4b4f0..5ab14ae7 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Okio.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/util/lang/Okio.kt @@ -9,4 +9,4 @@ package ca.gosyer.jui.ui.util.lang import io.ktor.utils.io.ByteReadChannel import okio.Source -expect fun ByteReadChannel.toSource(): Source +expect suspend fun ByteReadChannel.toSource(): Source diff --git a/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/DesktopLicenses.kt b/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/DesktopLicenses.kt index 7a5ffcd1..41eda2d2 100644 --- a/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/DesktopLicenses.kt +++ b/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/DesktopLicenses.kt @@ -18,6 +18,7 @@ import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.ui.compose.Libraries import com.mikepenz.aboutlibraries.ui.compose.LibraryColors +import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults import kotlinx.collections.immutable.ImmutableList @Composable @@ -33,6 +34,10 @@ actual fun getLicenses(): Libs? { return libs } +actual typealias LibraryDefaults = LibraryDefaults + +actual typealias LibraryColors = LibraryColors + @Composable actual fun InternalAboutLibraries( libraries: ImmutableList, diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/IosViewModelComponent.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/IosViewModelComponent.kt new file mode 100644 index 00000000..80b4dfa0 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/IosViewModelComponent.kt @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisallowComposableCalls +import androidx.compose.runtime.saveable.rememberSaveable +import ca.gosyer.jui.ui.base.LocalViewModels +import ca.gosyer.jui.ui.base.state.SavedStateHandle +import ca.gosyer.jui.uicore.vm.ViewModel +import cafe.adriel.voyager.core.model.rememberScreenModel +import cafe.adriel.voyager.core.screen.Screen + +actual interface ViewModelComponent : SharedViewModelComponent + +@Composable +actual inline fun Screen.stateViewModel( + tag: String?, + crossinline factory: @DisallowComposableCalls ViewModelComponent.(SavedStateHandle) -> VM +): VM { + val viewModelFactory = LocalViewModels.current + val savedStateHandle = rememberSaveable { SavedStateHandle() } + return rememberScreenModel(tag) { viewModelFactory.factory(savedStateHandle) } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/components/IosTooltipArea.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/components/IosTooltipArea.kt new file mode 100644 index 00000000..8cb7eff6 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/components/IosTooltipArea.kt @@ -0,0 +1,41 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset + +actual interface TooltipPlacement + +actual class CursorPoint actual constructor( + offset: DpOffset, + alignment: Alignment, + windowMargin: Dp +) : TooltipPlacement + +actual class ComponentRect actual constructor( + anchor: Alignment, + alignment: Alignment, + offset: DpOffset +) : TooltipPlacement + +@Composable +actual fun TooltipArea( + tooltip: @Composable () -> Unit, + modifier: Modifier, + delayMillis: Int, + tooltipPlacement: TooltipPlacement, + content: @Composable () -> Unit +) { + Box(Modifier) { + content() + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileChooser.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileChooser.kt new file mode 100644 index 00000000..606300f4 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileChooser.kt @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.file + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import okio.Source + +actual class FileChooser(private val onFileFound: (Source) -> Unit) { + actual fun launch(extension: String) { + TODO() + } +} + +@Composable +actual fun rememberFileChooser(onFileFound: (Source) -> Unit): FileChooser { + return remember { FileChooser(onFileFound) } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileSaver.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileSaver.kt new file mode 100644 index 00000000..35e851e7 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/file/IosFileSaver.kt @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.file + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import okio.Sink + +actual class FileSaver( + private val onFileSelected: (Sink) -> Unit, + private val onCancel: () -> Unit, + private val onError: () -> Unit, +) { + actual fun save(name: String) { + TODO() + } +} + +@Composable +actual fun rememberFileSaver( + onFileSelected: (Sink) -> Unit, + onCancel: () -> Unit, + onError: () -> Unit +): FileSaver { + return remember { FileSaver(onFileSelected, onCancel, onError) } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosBitmapDecoderFactory.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosBitmapDecoderFactory.kt new file mode 100644 index 00000000..260338fe --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosBitmapDecoderFactory.kt @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.image + +import ca.gosyer.jui.uicore.vm.ContextWrapper +import com.seiko.imageloader.component.decoder.Decoder +import com.seiko.imageloader.component.decoder.SkiaImageDecoder + +actual class BitmapDecoderFactory actual constructor(contextWrapper: ContextWrapper) : + Decoder.Factory by SkiaImageDecoder.Factory() diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt new file mode 100644 index 00000000..dcd172e1 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt @@ -0,0 +1,44 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.image + +import ca.gosyer.jui.uicore.vm.ContextWrapper +import com.seiko.imageloader.ImageLoaderBuilder +import com.seiko.imageloader.cache.disk.DiskCache +import com.seiko.imageloader.cache.disk.DiskCacheBuilder +import com.seiko.imageloader.cache.memory.MemoryCache +import com.seiko.imageloader.cache.memory.MemoryCacheBuilder +import com.seiko.imageloader.request.Options +import okio.Path.Companion.toPath +import platform.Foundation.NSCachesDirectory +import platform.Foundation.NSFileManager +import platform.Foundation.NSUserDomainMask + +actual val imageConfig: Options.ImageConfig = Options.ImageConfig.ARGB_8888 + +actual fun imageLoaderBuilder(contextWrapper: ContextWrapper): ImageLoaderBuilder { + return ImageLoaderBuilder() +} + +actual fun diskCache(contextWrapper: ContextWrapper, cacheDir: String): DiskCache { + return DiskCacheBuilder() + .directory(getCacheDir().toPath() / cacheDir) + .maxSizeBytes(1024 * 1024 * 150) // 150 MB + .build() +} + +private fun getCacheDir(): String { + return NSFileManager.defaultManager.URLForDirectory( + NSCachesDirectory, NSUserDomainMask, null, true, null + )!!.path.orEmpty() +} + +actual fun memoryCache(contextWrapper: ContextWrapper): MemoryCache { + return MemoryCacheBuilder() + .maxSizePercent(0.25) + .build() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/ActionIcon.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/ActionIcon.kt new file mode 100644 index 00000000..1d87a0dc --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/ActionIcon.kt @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.navigation + +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector + +// todo +@Composable +actual fun ActionIcon(onClick: () -> Unit, contentDescription: String, icon: ImageVector) { + IconButton(onClick = onClick) { + Icon(icon, contentDescription) + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/IosBackHandler.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/IosBackHandler.kt new file mode 100644 index 00000000..5ca5d8ee --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/navigation/IosBackHandler.kt @@ -0,0 +1,12 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.navigation + +import androidx.compose.runtime.Composable + +@Composable +actual fun BackHandler(enabled: Boolean, onBack: () -> Unit) {} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt new file mode 100644 index 00000000..55d1a0a5 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt @@ -0,0 +1,37 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.prefs + +import androidx.compose.ui.graphics.Color +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.value +import platform.CoreGraphics.CGFloatVar +import platform.UIKit.UIColor + +fun Color.toUIColor() = UIColor(red.toDouble(), green.toDouble(), blue.toDouble(), 1.0) + +internal actual fun Color.toHsv(): FloatArray = memScoped { + val uiColor = toUIColor() + val hue = alloc() + val saturation = alloc() + val brightness = alloc() + val alpha = alloc() + uiColor.getHue(hue.ptr, saturation.ptr, brightness.ptr, alpha.ptr) + + floatArrayOf(hue.value.toFloat(), saturation.value.toFloat(), brightness.value.toFloat()) +} + +internal actual fun hexStringToColor(hex: String): Color? { + return try { + val i = hex.removePrefix("#").toInt(16) + Color(i shr 16 and 0xFF, i shr 8 and 0xFF, i and 0xFF) + } catch (e: Exception) { + null + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/screen/BaseScreen.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/screen/BaseScreen.kt new file mode 100644 index 00000000..31471b90 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/screen/BaseScreen.kt @@ -0,0 +1,16 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.screen + +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.core.screen.ScreenKey +import cafe.adriel.voyager.core.screen.uniqueScreenKey + +actual abstract class BaseScreen : Screen { + + override val key: ScreenKey = uniqueScreenKey +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/state/IosSavedStateHandle.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/state/IosSavedStateHandle.kt new file mode 100644 index 00000000..1bd5685d --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/state/IosSavedStateHandle.kt @@ -0,0 +1,50 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.state + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +actual class SavedStateHandle { + private val regular = mutableMapOf() + private val flows = mutableMapOf>() + + actual operator fun get(key: String): T? { + @Suppress("UNCHECKED_CAST") + return regular[key] as T? + } + + actual operator fun set(key: String, value: T?) { + regular[key] = value + flows[key]?.value = value + } + + actual fun remove(key: String): T? { + @Suppress("UNCHECKED_CAST") + val latestValue = regular.remove(key) as T? + flows.remove(key) + return latestValue + } + + actual fun getStateFlow( + key: String, + initialValue: T + ): StateFlow { + @Suppress("UNCHECKED_CAST") + // If a flow exists we should just return it, and since it is a StateFlow and a value must + // always be set, we know a value must already be available + return flows.getOrPut(key) { + // If there is not a value associated with the key, add the initial value, otherwise, + // use the one we already have. + if (!regular.containsKey(key)) { + regular[key] = initialValue + } + MutableStateFlow(regular[key]).apply { flows[key] = this } + }.asStateFlow() as StateFlow + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/theme/ThemeScrollbarStyle.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/theme/ThemeScrollbarStyle.kt new file mode 100644 index 00000000..f84fe5f1 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/theme/ThemeScrollbarStyle.kt @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.base.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import ca.gosyer.jui.uicore.components.ScrollbarStyle + +actual object ThemeScrollbarStyle { + private val defaultScrollbarStyle = ScrollbarStyle() + + @Stable + @Composable + actual fun getScrollbarStyle(): ScrollbarStyle { + return defaultScrollbarStyle + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/categories/OpenCategories.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/categories/OpenCategories.kt new file mode 100644 index 00000000..c0a3cebc --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/categories/OpenCategories.kt @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.categories + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.Navigator + +actual class CategoriesLauncher(private val navigator: Navigator?) { + + actual fun open() { + navigator?.push(CategoriesScreen()) + } + + @Composable + actual fun CategoriesWindow() { + } +} + +@Composable +actual fun rememberCategoriesLauncher(notifyFinished: () -> Unit): CategoriesLauncher { + val navigator = LocalNavigator.current + return remember(navigator) { CategoriesLauncher(navigator) } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/downloads/IosDownloadService.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/downloads/IosDownloadService.kt new file mode 100644 index 00000000..ff4bc794 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/downloads/IosDownloadService.kt @@ -0,0 +1,19 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.downloads + +import ca.gosyer.jui.domain.base.WebsocketService +import ca.gosyer.jui.domain.download.service.DownloadService +import ca.gosyer.jui.uicore.vm.ContextWrapper + +internal actual fun startDownloadService( + contextWrapper: ContextWrapper, + downloadService: DownloadService, + actions: WebsocketService.Actions +) { + downloadService.init() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/library/components/IosLibraryGrid.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/library/components/IosLibraryGrid.kt new file mode 100644 index 00000000..cb682a20 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/library/components/IosLibraryGrid.kt @@ -0,0 +1,51 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.library.components + +import androidx.compose.foundation.combinedClickable +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import ca.gosyer.jui.i18n.MR +import ca.gosyer.jui.uicore.resources.stringResource + +actual fun Modifier.libraryMangaModifier( + onClickManga: () -> Unit, + onClickRemoveManga: () -> Unit +): Modifier = composed { + var expanded by remember { mutableStateOf(false) } + DropdownMenu( + expanded, + onDismissRequest = { expanded = false } + ) { + listOf( + stringResource(MR.strings.action_remove_favorite) to onClickRemoveManga + ).forEach { (label, onClick) -> + DropdownMenuItem( + onClick = { + expanded = false + onClick() + } + ) { + Text(text = label) + } + } + } + + Modifier.combinedClickable( + onClick = { onClickManga() }, + onLongClick = { + expanded = true + } + ) +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/components/getDebugInfo.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/components/getDebugInfo.kt new file mode 100644 index 00000000..7f809296 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/components/getDebugInfo.kt @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.main.about.components + +import ca.gosyer.jui.presentation.build.BuildKonfig +import platform.UIKit.UIDevice + +actual fun getDebugInfo(): String { + val device = UIDevice.currentDevice + return """ + App version: ${BuildKonfig.VERSION} (${ if (BuildKonfig.DEBUG) "Debug" else "Standard"}, ${BuildKonfig.MIGRATION_CODE}) + Preview build: r${BuildKonfig.PREVIEW_BUILD} + Device name: ${device.name} + Device model: ${device.model} + System name: ${device.systemName} + System version: ${device.systemVersion} + """.trimIndent() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/IosLicenses.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/IosLicenses.kt new file mode 100644 index 00000000..2e2ad504 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/about/licenses/components/IosLicenses.kt @@ -0,0 +1,74 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.main.about.licenses.components + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import ca.gosyer.jui.core.lang.withIOContext +import ca.gosyer.jui.i18n.MR +import com.mikepenz.aboutlibraries.Libs +import com.mikepenz.aboutlibraries.entity.Library +import kotlinx.collections.immutable.ImmutableList + +@Composable +actual fun getLicenses(): Libs? { + val libs by produceState( + null + ) { + withIOContext { + val json = MR.files.aboutlibraries.readText() + value = Libs.Builder().withJson(json).build() + } + } + return libs +} + +actual object LibraryDefaults { + @Composable + actual fun libraryColors( + backgroundColor: Color, + contentColor: Color, + badgeBackgroundColor: Color, + badgeContentColor: Color, + ): LibraryColors = object : LibraryColors {} + + actual val ContentPadding: PaddingValues get() = PaddingValues() +} + +actual interface LibraryColors + +@Composable +actual fun InternalAboutLibraries( + libraries: ImmutableList, + modifier: Modifier, + lazyListState: LazyListState, + contentPadding: PaddingValues, + showAuthor: Boolean, + showVersion: Boolean, + showLicenseBadges: Boolean, + colors: LibraryColors, + itemContentPadding: PaddingValues, + onLibraryClick: ((Library) -> Unit)? +) { + /*Libraries( + libraries = libraries, + modifier = modifier, + lazyListState = lazyListState, + contentPadding = contentPadding, + showAuthor = showAuthor, + showVersion = showVersion, + showLicenseBadges = showLicenseBadges, + colors = colors, + itemContentPadding = itemContentPadding, + onLibraryClick = onLibraryClick + )*/ +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/DebugOverlayViewModel.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/DebugOverlayViewModel.kt new file mode 100644 index 00000000..707e8e66 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/DebugOverlayViewModel.kt @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.main.components + +import ca.gosyer.jui.uicore.vm.ContextWrapper +import ca.gosyer.jui.uicore.vm.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import me.tatarka.inject.annotations.Inject + +actual class DebugOverlayViewModel @Inject constructor(contextWrapper: ContextWrapper) : ViewModel(contextWrapper) { + actual val maxMemory: String + get() = "" + actual val usedMemoryFlow: MutableStateFlow = MutableStateFlow("") +} \ No newline at end of file diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/IosLibraryUpdatesService.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/IosLibraryUpdatesService.kt new file mode 100644 index 00000000..bdf7b6bb --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/main/components/IosLibraryUpdatesService.kt @@ -0,0 +1,19 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.main.components + +import ca.gosyer.jui.domain.base.WebsocketService +import ca.gosyer.jui.domain.library.service.LibraryUpdateService +import ca.gosyer.jui.uicore.vm.ContextWrapper + +internal actual fun startLibraryUpdatesService( + contextWrapper: ContextWrapper, + libraryUpdatesService: LibraryUpdateService, + actions: WebsocketService.Actions +) { + libraryUpdatesService.init() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/manga/components/IosChapterItem.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/manga/components/IosChapterItem.kt new file mode 100644 index 00000000..17d90f07 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/manga/components/IosChapterItem.kt @@ -0,0 +1,24 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.manga.components + +import androidx.compose.foundation.combinedClickable +import androidx.compose.ui.Modifier + +actual fun Modifier.chapterItemModifier( + onClick: () -> Unit, + markRead: (() -> Unit)?, + markUnread: (() -> Unit)?, + bookmarkChapter: (() -> Unit)?, + unBookmarkChapter: (() -> Unit)?, + markPreviousAsRead: () -> Unit, + onSelectChapter: (() -> Unit)?, + onUnselectChapter: (() -> Unit)? +): Modifier = combinedClickable( + onClick = onUnselectChapter ?: onClick, + onLongClick = onSelectChapter +) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt new file mode 100644 index 00000000..c14f75c1 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt @@ -0,0 +1,50 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.reader + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import ca.gosyer.jui.ui.base.model.StableHolder +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.Navigator +import cafe.adriel.voyager.navigator.currentOrThrow +import kotlinx.coroutines.flow.MutableSharedFlow + +class ReaderScreen(val chapterIndex: Int, val mangaId: Long) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + ReaderMenu( + chapterIndex, + mangaId, + remember { StableHolder(MutableSharedFlow()) }, + navigator::pop + ) + } +} + +actual class ReaderLauncher(private val navigator: Navigator?) { + + actual fun launch( + chapterIndex: Int, + mangaId: Long + ) { + navigator?.push(ReaderScreen(chapterIndex, mangaId)) + } + + @Composable + actual fun Reader() { + } +} + +@Composable +actual fun rememberReaderLauncher(): ReaderLauncher { + val navigator = LocalNavigator.current + return remember(navigator) { ReaderLauncher(navigator) } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsAppearenceScreen.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsAppearenceScreen.kt new file mode 100644 index 00000000..b470cd7d --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsAppearenceScreen.kt @@ -0,0 +1,9 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.settings + +actual val showWindowDecorationsOption: Boolean = false diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsServerScreen.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsServerScreen.kt new file mode 100644 index 00000000..47bfbdb4 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/settings/IosSettingsServerScreen.kt @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.settings + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import ca.gosyer.jui.uicore.vm.ContextWrapper +import ca.gosyer.jui.uicore.vm.ViewModel +import me.tatarka.inject.annotations.Inject + +@Composable +actual fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit { + return {} +} + +actual class SettingsServerHostViewModel @Inject constructor(contextWrapper: ContextWrapper) : ViewModel(contextWrapper) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/sources/components/IosSourcesMenu.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/sources/components/IosSourcesMenu.kt new file mode 100644 index 00000000..60cacfaf --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/sources/components/IosSourcesMenu.kt @@ -0,0 +1,17 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.sources.components + +import androidx.compose.foundation.clickable +import androidx.compose.ui.Modifier + +actual fun Modifier.sourceSideMenuItem( + onSourceTabClick: () -> Unit, + onSourceCloseTabClick: () -> Unit +): Modifier = clickable( + onClick = onSourceTabClick +) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/updates/components/IosUpdatesItem.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/updates/components/IosUpdatesItem.kt new file mode 100644 index 00000000..cfc27fb2 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/updates/components/IosUpdatesItem.kt @@ -0,0 +1,23 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.updates.components + +import androidx.compose.foundation.combinedClickable +import androidx.compose.ui.Modifier + +actual fun Modifier.updatesItemModifier( + onClick: () -> Unit, + markRead: (() -> Unit)?, + markUnread: (() -> Unit)?, + bookmarkChapter: (() -> Unit)?, + unBookmarkChapter: (() -> Unit)?, + onSelectChapter: (() -> Unit)?, + onUnselectChapter: (() -> Unit)? +): Modifier = combinedClickable( + onClick = onUnselectChapter ?: onClick, + onLongClick = onSelectChapter +) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/Image.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/Image.kt new file mode 100644 index 00000000..6998402e --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/Image.kt @@ -0,0 +1,23 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.util.compose + +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asComposeImageBitmap +import androidx.compose.ui.graphics.toComposeImageBitmap +import io.ktor.client.call.body +import io.ktor.client.statement.HttpResponse +import org.jetbrains.skia.Image +import com.seiko.imageloader.Image as ImageLoaderImage + +actual suspend fun HttpResponse.toImageBitmap(): ImageBitmap { + return Image.makeFromEncoded(body()).toComposeImageBitmap() +} + +actual fun ImageLoaderImage.asImageBitmap(): ImageBitmap { + return asComposeImageBitmap() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/IosColor.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/IosColor.kt new file mode 100644 index 00000000..cae849f2 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/compose/IosColor.kt @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.util.compose + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import platform.Foundation.NSString +import platform.Foundation.stringWithFormat + +actual fun Color.toHexString(): String { + return NSString.stringWithFormat("#%06X", (0xFFFFFF and toArgb())) +} + +actual fun Color.toLong() = NSString.stringWithFormat("%06X", 0xFFFFFF and toArgb()).toLong(16) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt new file mode 100644 index 00000000..254e8954 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.util.lang + +import androidx.compose.ui.text.intl.Locale +import platform.Foundation.NSString +import platform.Foundation.localizedCaseInsensitiveCompare + +actual class CollatorComparator() : Comparator { + + actual constructor(locale: Locale) : this() + + actual override fun compare(source: String, target: String): Int { + @Suppress("CAST_NEVER_SUCCEEDS") + return (source as NSString).localizedCaseInsensitiveCompare(target).toInt() + } +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/IosOkio.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/IosOkio.kt new file mode 100644 index 00000000..aa16a316 --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/IosOkio.kt @@ -0,0 +1,16 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.util.lang + +import ca.gosyer.jui.core.io.source +import io.ktor.util.toByteArray +import io.ktor.utils.io.ByteReadChannel +import okio.Source + +actual suspend fun ByteReadChannel.toSource(): Source { + return this.toByteArray().source() +} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/StringFormat.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/StringFormat.kt new file mode 100644 index 00000000..5ca3fb1e --- /dev/null +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/util/lang/StringFormat.kt @@ -0,0 +1,70 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.jui.ui.util.lang + +import platform.Foundation.NSString +import platform.Foundation.stringWithFormat + +// Taken from https://github.com/icerockdev/moko-resources/blob/master/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/Utils.kt +actual fun stringFormat(string: String, vararg args: Any?): String { + // NSString format works with NSObjects via %@, we should change standard format to %@ + val objcFormat = string.replace(Regex("%((?:\\.|\\d|\\$)*)[abcdefs]"), "%$1@") + // bad but objc interop limited :( + // When calling variadic C functions spread operator is supported only for *arrayOf(...) + @Suppress("MagicNumber") + return when (args.size) { + 0 -> NSString.stringWithFormat(objcFormat) + 1 -> NSString.stringWithFormat(objcFormat, args[0]) + 2 -> NSString.stringWithFormat(objcFormat, args[0], args[1]) + 3 -> NSString.stringWithFormat(objcFormat, args[0], args[1], args[2]) + 4 -> NSString.stringWithFormat(objcFormat, args[0], args[1], args[2], args[3]) + 5 -> NSString.stringWithFormat(objcFormat, args[0], args[1], args[2], args[3], args[4]) + 6 -> NSString.stringWithFormat( + objcFormat, + args[0], + args[1], + args[2], + args[3], + args[4], + args[5] + ) + 7 -> NSString.stringWithFormat( + objcFormat, + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6] + ) + 8 -> NSString.stringWithFormat( + objcFormat, + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7] + ) + 9 -> NSString.stringWithFormat( + objcFormat, + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7], + args[8] + ) + else -> throw IllegalArgumentException("can't handle more then 9 arguments now") + } +} diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt index 410b8bc6..3df9031c 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/Collator.kt @@ -8,17 +8,17 @@ package ca.gosyer.jui.ui.util.lang import androidx.compose.ui.text.intl.Locale import ca.gosyer.jui.core.lang.toPlatform -import java.text.Collator as JvmCollator +import java.text.Collator -actual fun Collator(locale: Locale): Collator { - return Collator(JvmCollator.getInstance(locale.toPlatform())) -} +actual class CollatorComparator(private val collator: Collator) : Comparator { + + actual constructor(locale: Locale) : this(Collator.getInstance(locale.toPlatform())) -actual class Collator(private val jvmCollator: JvmCollator) { init { - jvmCollator.strength = JvmCollator.PRIMARY + collator.strength = Collator.PRIMARY } - actual fun compare(source: String, target: String): Int { - return jvmCollator.compare(source, target) + + actual override fun compare(source: String, target: String): Int { + return collator.compare(source, target) } } diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/JvmOkio.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/JvmOkio.kt index d3a88738..b51f747b 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/JvmOkio.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/jui/ui/util/lang/JvmOkio.kt @@ -11,6 +11,6 @@ import io.ktor.utils.io.jvm.javaio.toInputStream import okio.Source import okio.source -actual fun ByteReadChannel.toSource(): Source { +actual suspend fun ByteReadChannel.toSource(): Source { return toInputStream().source() } diff --git a/ui-core/src/iosArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt b/ui-core/src/iosArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt index 633402bb..02896c16 100644 --- a/ui-core/src/iosArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt +++ b/ui-core/src/iosArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt @@ -6,16 +6,8 @@ package ca.gosyer.jui.uicore.resources -import androidx.compose.runtime.Composable -import dev.icerock.moko.resources.PluralsResource -import dev.icerock.moko.resources.desc.Plural -import dev.icerock.moko.resources.desc.PluralFormatted -import dev.icerock.moko.resources.desc.StringDesc +import dev.icerock.moko.resources.desc.PluralFormattedStringDesc +import dev.icerock.moko.resources.desc.PluralStringDesc -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int): String = - StringDesc.Plural(resource, quantity).localized() - -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String = - StringDesc.PluralFormatted(resource, quantity, *args).localized() +actual fun PluralStringDesc.localized(): String = localized() +actual fun PluralFormattedStringDesc.localized() = localized() \ No newline at end of file diff --git a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt index 835eed71..06c84d9a 100644 --- a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt +++ b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt @@ -7,11 +7,19 @@ package ca.gosyer.jui.uicore.resources import androidx.compose.runtime.Composable +import dev.icerock.moko.resources.PluralsResource import dev.icerock.moko.resources.StringResource +import dev.icerock.moko.resources.desc.Plural +import dev.icerock.moko.resources.desc.PluralFormatted +import dev.icerock.moko.resources.desc.PluralFormattedStringDesc +import dev.icerock.moko.resources.desc.PluralStringDesc import dev.icerock.moko.resources.desc.Resource import dev.icerock.moko.resources.desc.ResourceFormatted import dev.icerock.moko.resources.desc.StringDesc +expect fun PluralStringDesc.localized(): String +expect fun PluralFormattedStringDesc.localized(): String + @Composable actual fun stringResource(resource: StringResource): String = StringDesc.Resource(resource).localized() @@ -20,7 +28,6 @@ actual fun stringResource(resource: StringResource): String = actual fun stringResource(resource: StringResource, vararg args: Any): String = StringDesc.ResourceFormatted(resource, *args).localized() -/* TODO the commonizer chokes on .localized() @Composable actual fun stringResource(resource: PluralsResource, quantity: Int): String = StringDesc.Plural(resource, quantity).localized() @@ -28,4 +35,3 @@ actual fun stringResource(resource: PluralsResource, quantity: Int): String = @Composable actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String = StringDesc.PluralFormatted(resource, quantity, *args).localized() -*/ diff --git a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/vm/ContextWrapper.kt b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/vm/ContextWrapper.kt index b38e4fd5..18fc5d44 100644 --- a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/vm/ContextWrapper.kt +++ b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/vm/ContextWrapper.kt @@ -6,17 +6,28 @@ package ca.gosyer.jui.uicore.vm +import ca.gosyer.jui.core.lang.launchDefault import dev.icerock.moko.resources.StringResource +import dev.icerock.moko.resources.desc.desc import dev.icerock.moko.resources.format +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow import me.tatarka.inject.annotations.Inject actual class ContextWrapper @Inject constructor() { + private val _toasts = MutableSharedFlow>() + val toasts = _toasts.asSharedFlow() + actual fun toPlatformString(stringResource: StringResource): String { - return stringResource.localized() + return stringResource.desc().localized() } actual fun toPlatformString(stringResource: StringResource, vararg args: Any): String { return stringResource.format(*args).localized() } actual fun toast(string: String, length: Length) { + GlobalScope.launchDefault { + _toasts.emit(string to length) + } } } diff --git a/ui-core/src/iosSimulatorArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt b/ui-core/src/iosSimulatorArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt index 633402bb..02896c16 100644 --- a/ui-core/src/iosSimulatorArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt +++ b/ui-core/src/iosSimulatorArm64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt @@ -6,16 +6,8 @@ package ca.gosyer.jui.uicore.resources -import androidx.compose.runtime.Composable -import dev.icerock.moko.resources.PluralsResource -import dev.icerock.moko.resources.desc.Plural -import dev.icerock.moko.resources.desc.PluralFormatted -import dev.icerock.moko.resources.desc.StringDesc +import dev.icerock.moko.resources.desc.PluralFormattedStringDesc +import dev.icerock.moko.resources.desc.PluralStringDesc -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int): String = - StringDesc.Plural(resource, quantity).localized() - -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String = - StringDesc.PluralFormatted(resource, quantity, *args).localized() +actual fun PluralStringDesc.localized(): String = localized() +actual fun PluralFormattedStringDesc.localized() = localized() \ No newline at end of file diff --git a/ui-core/src/iosX64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt b/ui-core/src/iosX64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt index 633402bb..4f8b3316 100644 --- a/ui-core/src/iosX64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt +++ b/ui-core/src/iosX64Main/kotlin/ca/gosyer/jui/uicore/resources/IosStringResource.kt @@ -6,16 +6,8 @@ package ca.gosyer.jui.uicore.resources -import androidx.compose.runtime.Composable -import dev.icerock.moko.resources.PluralsResource -import dev.icerock.moko.resources.desc.Plural -import dev.icerock.moko.resources.desc.PluralFormatted -import dev.icerock.moko.resources.desc.StringDesc +import dev.icerock.moko.resources.desc.PluralFormattedStringDesc +import dev.icerock.moko.resources.desc.PluralStringDesc -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int): String = - StringDesc.Plural(resource, quantity).localized() - -@Composable -actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String = - StringDesc.PluralFormatted(resource, quantity, *args).localized() +actual fun PluralStringDesc.localized(): String = localized() +actual fun PluralFormattedStringDesc.localized() = localized()