Start adding iOS support to presentation

This commit is contained in:
Syer10
2022-11-17 19:49:27 -05:00
parent 7b2a54d2bc
commit 719a0720b8
51 changed files with 977 additions and 64 deletions

View File

@@ -7,7 +7,9 @@
package ca.gosyer.jui.core.io package ca.gosyer.jui.core.io
import ca.gosyer.jui.core.lang.withIOContext import ca.gosyer.jui.core.lang.withIOContext
import okio.Buffer
import okio.BufferedSink import okio.BufferedSink
import okio.BufferedSource
import okio.FileSystem import okio.FileSystem
import okio.Path import okio.Path
import okio.Source import okio.Source
@@ -32,3 +34,7 @@ suspend fun Source.copyTo(sink: BufferedSink) {
} }
} }
} }
fun ByteArray.source(): BufferedSource {
return Buffer().write(this)
}

View File

@@ -25,6 +25,9 @@ kotlin {
} }
} }
} }
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets { sourceSets {
all { all {
@@ -53,13 +56,16 @@ kotlin {
api(libs.dateTime) api(libs.dateTime)
api(libs.immutableCollections) api(libs.immutableCollections)
api(libs.aboutLibraries.core) api(libs.aboutLibraries.core)
api(libs.aboutLibraries.ui)
api(projects.core) api(projects.core)
api(projects.i18n) api(projects.i18n)
api(projects.domain) api(projects.domain)
api(projects.data) api(projects.data)
api(projects.uiCore) 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("org.jetbrains.compose.ui:ui-util"))
api(compose.materialIconsExtended) api(compose.materialIconsExtended)
} }
@@ -75,6 +81,8 @@ kotlin {
dependsOn(commonMain) dependsOn(commonMain)
dependencies { dependencies {
api(kotlin("stdlib-jdk8")) api(kotlin("stdlib-jdk8"))
api(libs.aboutLibraries.ui)
api(compose.desktop.currentOs)
} }
} }
val jvmTest by creating { val jvmTest by creating {
@@ -107,6 +115,22 @@ kotlin {
val androidTest by getting { val androidTest by getting {
dependsOn(jvmTest) 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)
}
} }
} }

View File

@@ -18,6 +18,7 @@ import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.Libraries import com.mikepenz.aboutlibraries.ui.compose.Libraries
import com.mikepenz.aboutlibraries.ui.compose.LibraryColors import com.mikepenz.aboutlibraries.ui.compose.LibraryColors
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
import com.mikepenz.aboutlibraries.util.withContext import com.mikepenz.aboutlibraries.util.withContext
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@@ -35,6 +36,10 @@ actual fun getLicenses(): Libs? {
return libs return libs
} }
actual typealias LibraryDefaults = LibraryDefaults
actual typealias LibraryColors = LibraryColors
@Composable @Composable
actual fun InternalAboutLibraries( actual fun InternalAboutLibraries(
libraries: ImmutableList<Library>, libraries: ImmutableList<Library>,

View File

@@ -12,8 +12,6 @@ import androidx.compose.ui.Modifier
actual fun Modifier.sourceSideMenuItem( actual fun Modifier.sourceSideMenuItem(
onSourceTabClick: () -> Unit, onSourceTabClick: () -> Unit,
onSourceCloseTabClick: () -> Unit onSourceCloseTabClick: () -> Unit
): Modifier = Modifier.clickable( ): Modifier = clickable(
onClick = { onClick = onSourceTabClick
onSourceTabClick()
}
) )

View File

@@ -7,7 +7,10 @@
package ca.gosyer.jui.ui.base.state package ca.gosyer.jui.ui.base.state
import ca.gosyer.jui.uicore.vm.ViewModel import ca.gosyer.jui.uicore.vm.ViewModel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.internal.SynchronizedObject
import kotlinx.coroutines.internal.synchronized
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -17,14 +20,17 @@ fun <T> SavedStateHandle.getStateFlow(
return SavedStateHandleDelegate(this, initialValue) return SavedStateHandleDelegate(this, initialValue)
} }
@OptIn(InternalCoroutinesApi::class)
class SavedStateHandleDelegate<T>( class SavedStateHandleDelegate<T>(
private val savedStateHandle: SavedStateHandle, private val savedStateHandle: SavedStateHandle,
private val initialValue: () -> T private val initialValue: () -> T
) : ReadOnlyProperty<ViewModel, SavedStateHandleStateFlow<T>> { ) : ReadOnlyProperty<ViewModel, SavedStateHandleStateFlow<T>> {
private val synchronizedObject = SynchronizedObject()
private var item: SavedStateHandleStateFlow<T>? = null private var item: SavedStateHandleStateFlow<T>? = null
override fun getValue(thisRef: ViewModel, property: KProperty<*>): SavedStateHandleStateFlow<T> { override fun getValue(thisRef: ViewModel, property: KProperty<*>): SavedStateHandleStateFlow<T> {
return item ?: synchronized(this) { return item ?: synchronized(synchronizedObject) {
if (item == null) { if (item == null) {
savedStateHandle.getSavedStateFlow(property.name, initialValue) savedStateHandle.getSavedStateFlow(property.name, initialValue)
.also { item = it } .also { item = it }

View File

@@ -13,6 +13,7 @@ import ca.gosyer.jui.ui.viewModel
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey import cafe.adriel.voyager.core.screen.ScreenKey
import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.core.screen.uniqueScreenKey
import kotlin.jvm.Transient
expect class CategoriesLauncher { expect class CategoriesLauncher {

View File

@@ -25,7 +25,7 @@ import ca.gosyer.jui.domain.updates.interactor.UpdateLibrary
import ca.gosyer.jui.i18n.MR import ca.gosyer.jui.i18n.MR
import ca.gosyer.jui.ui.base.state.SavedStateHandle import ca.gosyer.jui.ui.base.state.SavedStateHandle
import ca.gosyer.jui.ui.base.state.getStateFlow 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.ContextWrapper
import ca.gosyer.jui.uicore.vm.ViewModel import ca.gosyer.jui.uicore.vm.ViewModel
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@@ -191,7 +191,7 @@ class LibraryScreenViewModel @Inject constructor(
val sortFn: (Manga, Manga) -> Int = when (sortMode) { val sortFn: (Manga, Manga) -> Int = when (sortMode) {
Sort.ALPHABETICAL -> { Sort.ALPHABETICAL -> {
val locale = Locale.current val locale = Locale.current
val collator = Collator(locale); val collator = CollatorComparator(locale);
{ a, b -> { a, b ->
collator.compare(a.title.toLowerCase(locale), b.title.toLowerCase(locale)) collator.compare(a.title.toLowerCase(locale), b.title.toLowerCase(locale))

View File

@@ -19,11 +19,14 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold import androidx.compose.material.Scaffold
import androidx.compose.material.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.jui.i18n.MR 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 ca.gosyer.jui.uicore.resources.stringResource
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library 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.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -60,6 +61,18 @@ internal expect fun InternalAboutLibraries(
onLibraryClick: ((Library) -> Unit)? 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 @Composable
fun AboutLibraries( fun AboutLibraries(
libraries: ImmutableList<Library>, libraries: ImmutableList<Library>,

View File

@@ -6,6 +6,8 @@
package ca.gosyer.jui.ui.reader.loader 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.PriorityChannel
import ca.gosyer.jui.core.lang.throwIfCancellation import ca.gosyer.jui.core.lang.throwIfCancellation
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
@@ -39,6 +41,7 @@ import kotlinx.coroutines.launch
import okio.BufferedSource import okio.BufferedSource
import okio.FileSystem import okio.FileSystem
import okio.buffer import okio.buffer
import okio.use
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class TachideskPageLoader( class TachideskPageLoader(

View File

@@ -36,8 +36,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp 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.copyTo
import ca.gosyer.jui.core.io.saveTo 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.core.lang.throwIfCancellation
import ca.gosyer.jui.domain.backup.interactor.ExportBackupFile import ca.gosyer.jui.domain.backup.interactor.ExportBackupFile
import ca.gosyer.jui.domain.backup.interactor.ImportBackupFile import ca.gosyer.jui.domain.backup.interactor.ImportBackupFile

View File

@@ -7,6 +7,7 @@
package ca.gosyer.jui.ui.sources.globalsearch package ca.gosyer.jui.ui.sources.globalsearch
import androidx.compose.runtime.snapshots.SnapshotStateMap 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.manga.model.Manga
import ca.gosyer.jui.domain.source.interactor.GetSearchManga import ca.gosyer.jui.domain.source.interactor.GetSearchManga
import ca.gosyer.jui.domain.source.interactor.GetSourceList import ca.gosyer.jui.domain.source.interactor.GetSourceList

View File

@@ -8,8 +8,6 @@ package ca.gosyer.jui.ui.util.lang
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.intl.Locale
expect fun Collator(locale: Locale): Collator expect class CollatorComparator(locale: Locale) : Comparator<String> {
override fun compare(source: String, target: String): Int
expect class Collator {
fun compare(source: String, target: String): Int
} }

View File

@@ -9,4 +9,4 @@ package ca.gosyer.jui.ui.util.lang
import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.ByteReadChannel
import okio.Source import okio.Source
expect fun ByteReadChannel.toSource(): Source expect suspend fun ByteReadChannel.toSource(): Source

View File

@@ -18,6 +18,7 @@ import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.Libraries import com.mikepenz.aboutlibraries.ui.compose.Libraries
import com.mikepenz.aboutlibraries.ui.compose.LibraryColors import com.mikepenz.aboutlibraries.ui.compose.LibraryColors
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Composable @Composable
@@ -33,6 +34,10 @@ actual fun getLicenses(): Libs? {
return libs return libs
} }
actual typealias LibraryDefaults = LibraryDefaults
actual typealias LibraryColors = LibraryColors
@Composable @Composable
actual fun InternalAboutLibraries( actual fun InternalAboutLibraries(
libraries: ImmutableList<Library>, libraries: ImmutableList<Library>,

View File

@@ -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 <reified VM : ViewModel> 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) }
}

View File

@@ -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()
}
}

View File

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

View File

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

View File

@@ -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()

View File

@@ -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()
}

View File

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

View File

@@ -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) {}

View File

@@ -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<CGFloatVar>()
val saturation = alloc<CGFloatVar>()
val brightness = alloc<CGFloatVar>()
val alpha = alloc<CGFloatVar>()
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
}
}

View File

@@ -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
}

View File

@@ -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<String, Any?>()
private val flows = mutableMapOf<String, MutableStateFlow<Any?>>()
actual operator fun <T> get(key: String): T? {
@Suppress("UNCHECKED_CAST")
return regular[key] as T?
}
actual operator fun <T> set(key: String, value: T?) {
regular[key] = value
flows[key]?.value = value
}
actual fun <T> remove(key: String): T? {
@Suppress("UNCHECKED_CAST")
val latestValue = regular.remove(key) as T?
flows.remove(key)
return latestValue
}
actual fun <T> getStateFlow(
key: String,
initialValue: T
): StateFlow<T> {
@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<T>
}
}

View File

@@ -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
}
}

View File

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

View File

@@ -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()
}

View File

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

View File

@@ -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()
}

View File

@@ -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<Libs?>(
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<Library>,
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
)*/
}

View File

@@ -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<String> = MutableStateFlow("")
}

View File

@@ -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()
}

View File

@@ -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
)

View File

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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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<ByteArray>()).toComposeImageBitmap()
}
actual fun ImageLoaderImage.asImageBitmap(): ImageBitmap {
return asComposeImageBitmap()
}

View File

@@ -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)

View File

@@ -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<String> {
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()
}
}

View File

@@ -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()
}

View File

@@ -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")
}
}

View File

@@ -8,17 +8,17 @@ package ca.gosyer.jui.ui.util.lang
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.intl.Locale
import ca.gosyer.jui.core.lang.toPlatform import ca.gosyer.jui.core.lang.toPlatform
import java.text.Collator as JvmCollator import java.text.Collator
actual fun Collator(locale: Locale): Collator { actual class CollatorComparator(private val collator: Collator) : Comparator<String> {
return Collator(JvmCollator.getInstance(locale.toPlatform()))
} actual constructor(locale: Locale) : this(Collator.getInstance(locale.toPlatform()))
actual class Collator(private val jvmCollator: JvmCollator) {
init { 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)
} }
} }

View File

@@ -11,6 +11,6 @@ import io.ktor.utils.io.jvm.javaio.toInputStream
import okio.Source import okio.Source
import okio.source import okio.source
actual fun ByteReadChannel.toSource(): Source { actual suspend fun ByteReadChannel.toSource(): Source {
return toInputStream().source() return toInputStream().source()
} }

View File

@@ -6,16 +6,8 @@
package ca.gosyer.jui.uicore.resources package ca.gosyer.jui.uicore.resources
import androidx.compose.runtime.Composable import dev.icerock.moko.resources.desc.PluralFormattedStringDesc
import dev.icerock.moko.resources.PluralsResource import dev.icerock.moko.resources.desc.PluralStringDesc
import dev.icerock.moko.resources.desc.Plural
import dev.icerock.moko.resources.desc.PluralFormatted
import dev.icerock.moko.resources.desc.StringDesc
@Composable actual fun PluralStringDesc.localized(): String = localized()
actual fun stringResource(resource: PluralsResource, quantity: Int): String = actual fun PluralFormattedStringDesc.localized() = localized()
StringDesc.Plural(resource, quantity).localized()
@Composable
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
StringDesc.PluralFormatted(resource, quantity, *args).localized()

View File

@@ -7,11 +7,19 @@
package ca.gosyer.jui.uicore.resources package ca.gosyer.jui.uicore.resources
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import dev.icerock.moko.resources.PluralsResource
import dev.icerock.moko.resources.StringResource 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.Resource
import dev.icerock.moko.resources.desc.ResourceFormatted import dev.icerock.moko.resources.desc.ResourceFormatted
import dev.icerock.moko.resources.desc.StringDesc import dev.icerock.moko.resources.desc.StringDesc
expect fun PluralStringDesc.localized(): String
expect fun PluralFormattedStringDesc.localized(): String
@Composable @Composable
actual fun stringResource(resource: StringResource): String = actual fun stringResource(resource: StringResource): String =
StringDesc.Resource(resource).localized() StringDesc.Resource(resource).localized()
@@ -20,7 +28,6 @@ actual fun stringResource(resource: StringResource): String =
actual fun stringResource(resource: StringResource, vararg args: Any): String = actual fun stringResource(resource: StringResource, vararg args: Any): String =
StringDesc.ResourceFormatted(resource, *args).localized() StringDesc.ResourceFormatted(resource, *args).localized()
/* TODO the commonizer chokes on .localized()
@Composable @Composable
actual fun stringResource(resource: PluralsResource, quantity: Int): String = actual fun stringResource(resource: PluralsResource, quantity: Int): String =
StringDesc.Plural(resource, quantity).localized() StringDesc.Plural(resource, quantity).localized()
@@ -28,4 +35,3 @@ actual fun stringResource(resource: PluralsResource, quantity: Int): String =
@Composable @Composable
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String = actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
StringDesc.PluralFormatted(resource, quantity, *args).localized() StringDesc.PluralFormatted(resource, quantity, *args).localized()
*/

View File

@@ -6,17 +6,28 @@
package ca.gosyer.jui.uicore.vm package ca.gosyer.jui.uicore.vm
import ca.gosyer.jui.core.lang.launchDefault
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.desc.desc
import dev.icerock.moko.resources.format 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 import me.tatarka.inject.annotations.Inject
actual class ContextWrapper @Inject constructor() { actual class ContextWrapper @Inject constructor() {
private val _toasts = MutableSharedFlow<Pair<String, Length>>()
val toasts = _toasts.asSharedFlow()
actual fun toPlatformString(stringResource: StringResource): String { actual fun toPlatformString(stringResource: StringResource): String {
return stringResource.localized() return stringResource.desc().localized()
} }
actual fun toPlatformString(stringResource: StringResource, vararg args: Any): String { actual fun toPlatformString(stringResource: StringResource, vararg args: Any): String {
return stringResource.format(*args).localized() return stringResource.format(*args).localized()
} }
actual fun toast(string: String, length: Length) { actual fun toast(string: String, length: Length) {
GlobalScope.launchDefault {
_toasts.emit(string to length)
}
} }
} }

View File

@@ -6,16 +6,8 @@
package ca.gosyer.jui.uicore.resources package ca.gosyer.jui.uicore.resources
import androidx.compose.runtime.Composable import dev.icerock.moko.resources.desc.PluralFormattedStringDesc
import dev.icerock.moko.resources.PluralsResource import dev.icerock.moko.resources.desc.PluralStringDesc
import dev.icerock.moko.resources.desc.Plural
import dev.icerock.moko.resources.desc.PluralFormatted
import dev.icerock.moko.resources.desc.StringDesc
@Composable actual fun PluralStringDesc.localized(): String = localized()
actual fun stringResource(resource: PluralsResource, quantity: Int): String = actual fun PluralFormattedStringDesc.localized() = localized()
StringDesc.Plural(resource, quantity).localized()
@Composable
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
StringDesc.PluralFormatted(resource, quantity, *args).localized()

View File

@@ -6,16 +6,8 @@
package ca.gosyer.jui.uicore.resources package ca.gosyer.jui.uicore.resources
import androidx.compose.runtime.Composable import dev.icerock.moko.resources.desc.PluralFormattedStringDesc
import dev.icerock.moko.resources.PluralsResource import dev.icerock.moko.resources.desc.PluralStringDesc
import dev.icerock.moko.resources.desc.Plural
import dev.icerock.moko.resources.desc.PluralFormatted
import dev.icerock.moko.resources.desc.StringDesc
@Composable actual fun PluralStringDesc.localized(): String = localized()
actual fun stringResource(resource: PluralsResource, quantity: Int): String = actual fun PluralFormattedStringDesc.localized() = localized()
StringDesc.Plural(resource, quantity).localized()
@Composable
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
StringDesc.PluralFormatted(resource, quantity, *args).localized()