mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-23 13:02:33 +01:00
Add iOS module
This commit is contained in:
41
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/AppComponent.kt
Normal file
41
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/AppComponent.kt
Normal 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.ios
|
||||
|
||||
import ca.gosyer.jui.core.di.AppScope
|
||||
import ca.gosyer.jui.data.DataComponent
|
||||
import ca.gosyer.jui.domain.DomainComponent
|
||||
import ca.gosyer.jui.ui.ViewModelComponent
|
||||
import ca.gosyer.jui.ui.base.UiComponent
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import me.tatarka.inject.annotations.Component
|
||||
import me.tatarka.inject.annotations.Provides
|
||||
|
||||
@AppScope
|
||||
@Component
|
||||
abstract class AppComponent(
|
||||
@get:Provides
|
||||
val context: ContextWrapper
|
||||
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
|
||||
|
||||
abstract val appMigrations: AppMigrations
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
protected val appMigrationsFactory: AppMigrations
|
||||
get() = AppMigrations(migrationPreferences, contextWrapper)
|
||||
|
||||
val bind: ViewModelComponent
|
||||
@Provides get() = this
|
||||
|
||||
companion object {
|
||||
private var appComponentInstance: AppComponent? = null
|
||||
|
||||
fun getInstance(context: ContextWrapper) = appComponentInstance ?: create(context)
|
||||
.also { appComponentInstance = it }
|
||||
}
|
||||
}
|
||||
32
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/AppMigrations.kt
Normal file
32
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/AppMigrations.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.ios
|
||||
|
||||
import ca.gosyer.jui.domain.migration.service.MigrationPreferences
|
||||
import ca.gosyer.jui.ios.build.BuildKonfig
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class AppMigrations @Inject constructor(
|
||||
private val migrationPreferences: MigrationPreferences,
|
||||
private val contextWrapper: ContextWrapper
|
||||
) {
|
||||
|
||||
fun runMigrations(): Boolean {
|
||||
val oldVersion = migrationPreferences.appVersion().get()
|
||||
if (oldVersion < BuildKonfig.MIGRATION_CODE) {
|
||||
migrationPreferences.appVersion().set(BuildKonfig.MIGRATION_CODE)
|
||||
|
||||
// Fresh install
|
||||
if (oldVersion == 0) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
146
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt
Normal file
146
ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package ca.gosyer.jui.ios
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Application
|
||||
import ca.gosyer.jui.ui.base.theme.AppTheme
|
||||
import ca.gosyer.jui.ui.main.MainMenu
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.Length
|
||||
import kotlinx.cinterop.autoreleasepool
|
||||
import kotlinx.cinterop.cstr
|
||||
import kotlinx.cinterop.memScoped
|
||||
import kotlinx.cinterop.toCValues
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import platform.Foundation.NSStringFromClass
|
||||
import platform.UIKit.UIApplication
|
||||
import platform.UIKit.UIApplicationDelegateProtocol
|
||||
import platform.UIKit.UIApplicationDelegateProtocolMeta
|
||||
import platform.UIKit.UIApplicationMain
|
||||
import platform.UIKit.UIResponder
|
||||
import platform.UIKit.UIResponderMeta
|
||||
import platform.UIKit.UIScreen
|
||||
import platform.UIKit.UIWindow
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
fun main() {
|
||||
val args = emptyArray<String>()
|
||||
memScoped {
|
||||
val argc = args.size + 1
|
||||
val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues()
|
||||
autoreleasepool {
|
||||
UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SkikoAppDelegate @OverrideInit constructor() : UIResponder(), UIApplicationDelegateProtocol {
|
||||
companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta
|
||||
|
||||
private var _window: UIWindow? = null
|
||||
override fun window() = _window
|
||||
override fun setWindow(window: UIWindow?) {
|
||||
_window = window
|
||||
}
|
||||
|
||||
private val context = ContextWrapper()
|
||||
|
||||
private val appComponent = AppComponent.getInstance(context)
|
||||
|
||||
init {
|
||||
appComponent.migrations.runMigrations()
|
||||
appComponent.appMigrations.runMigrations()
|
||||
|
||||
appComponent.downloadService.init()
|
||||
appComponent.libraryUpdateService.init()
|
||||
}
|
||||
|
||||
val uiHooks = appComponent.hooks
|
||||
|
||||
override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {
|
||||
window = UIWindow(frame = UIScreen.mainScreen.bounds).apply {
|
||||
rootViewController = Application("Tachidesk-JUI") {
|
||||
CompositionLocalProvider(*uiHooks) {
|
||||
AppTheme {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
MainMenu()
|
||||
ToastOverlay(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 64.dp),
|
||||
context = context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
makeKeyAndVisible()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ToastOverlay(modifier: Modifier, context: ContextWrapper) {
|
||||
var toast by remember { mutableStateOf<Pair<String, Length>?>(null) }
|
||||
LaunchedEffect(Unit) {
|
||||
context.toasts
|
||||
.onEach {
|
||||
toast = it
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
LaunchedEffect(toast) {
|
||||
if (toast != null) {
|
||||
delay(
|
||||
when (toast?.second) {
|
||||
Length.SHORT -> 2.seconds
|
||||
Length.LONG -> 5.seconds
|
||||
else -> Duration.ZERO
|
||||
}
|
||||
)
|
||||
toast = null
|
||||
}
|
||||
}
|
||||
@Suppress("NAME_SHADOWING")
|
||||
(Crossfade(
|
||||
toast?.first,
|
||||
modifier = modifier
|
||||
) { toast ->
|
||||
if (toast != null) {
|
||||
Card(
|
||||
Modifier.sizeIn(maxWidth = 200.dp),
|
||||
shape = CircleShape,
|
||||
backgroundColor = Color.DarkGray
|
||||
) {
|
||||
Text(
|
||||
toast,
|
||||
Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
color = Color.White,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
fontSize = 12.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user