Massive rewrite

- Replace toothpick with kotlin-inject
- Create a data module
- Fix bugs from last rewrite
- Start replacing java.nio.Path with okio.Path
This commit is contained in:
Syer10
2022-01-29 15:36:48 -05:00
parent 9ad1baa36a
commit 8edd05aafe
172 changed files with 902 additions and 738 deletions

View File

@@ -1,11 +1,17 @@
import Config.migrationCode
import Config.serverCode
import Config.tachideskVersion
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type
plugins {
kotlin("multiplatform") version "1.6.10" apply false
kotlin("kapt") version "1.6.10" apply false
kotlin("plugin.serialization") version "1.6.10" apply false
id("com.android.library") version "7.0.4" apply false
id("com.android.application") version "7.0.4" apply false
id("org.jetbrains.compose") version "1.0.1" apply false
id("com.google.devtools.ksp") version "1.6.10-1.0.2"
id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
id("com.codingfeline.buildkonfig") version "0.11.0" apply false
id("dev.icerock.mobile.multiplatform-resources") version "0.18.0" apply false
id("org.jmailen.kotlinter") version "3.8.0" apply false
id("com.github.ben-manes.versions") version "0.41.0"
@@ -34,6 +40,21 @@ allprojects {
}
subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + listOf(
"-Xjvm-default=compatibility",
)
}
}
tasks.withType<org.jmailen.gradle.kotlinter.tasks.LintTask> {
source(files("src"))
exclude("ca/gosyer/*/build")
}
tasks.withType<org.jmailen.gradle.kotlinter.tasks.FormatTask> {
source(files("src"))
exclude("ca/gosyer/*/build")
}
plugins.withType<com.android.build.gradle.BasePlugin> {
configure<com.android.build.gradle.BaseExtension> {
compileSdkVersion(31)
@@ -47,7 +68,7 @@ subprojects {
}*/
}
compileOptions {
//isCoreLibraryDesugaringEnabled = true
isCoreLibraryDesugaringEnabled = true
sourceCompatibility(JavaVersion.VERSION_11)
targetCompatibility(JavaVersion.VERSION_11)
}
@@ -60,7 +81,38 @@ subprojects {
}
}
dependencies {
//add("coreLibraryDesugaring", Deps.desugarJdkLibs)
add("coreLibraryDesugaring", libs.desugarJdkLibs)
}
}
}
plugins.withType<com.codingfeline.buildkonfig.gradle.BuildKonfigPlugin> {
configure<com.codingfeline.buildkonfig.gradle.BuildKonfigExtension> {
defaultConfigs {
buildConfigField(Type.STRING, "NAME", rootProject.name)
buildConfigField(Type.STRING, "VERSION", project.version.toString())
buildConfigField(Type.INT, "MIGRATION_CODE", migrationCode.toString())
buildConfigField(Type.BOOLEAN, "DEBUG", project.hasProperty("debugApp").toString())
buildConfigField(Type.BOOLEAN, "IS_PREVIEW", project.hasProperty("preview").toString())
buildConfigField(Type.INT, "PREVIEW_BUILD", project.properties["preview"]?.toString()?.trim('"') ?: 0.toString())
// Tachidesk
buildConfigField(Type.STRING, "TACHIDESK_SP_VERSION", tachideskVersion)
buildConfigField(Type.INT, "SERVER_CODE", serverCode.toString())
}
}
}
plugins.withType<org.jmailen.gradle.kotlinter.KotlinterPlugin> {
configure<org.jmailen.gradle.kotlinter.KotlinterExtension> {
experimentalRules = true
disabledRules = arrayOf("experimental:argument-list-wrapping", "experimental:trailing-comma")
}
}
plugins.withType<com.google.devtools.ksp.gradle.KspGradleSubplugin> {
configure<com.google.devtools.ksp.gradle.KspExtension> {
arg("me.tatarka.inject.generateCompanionExtensions", "true")
if (project.hasProperty("debugApp")) {
arg("me.tatarka.inject.dumpGraph", "true")
}
}
}

View File

@@ -10,5 +10,5 @@ object Config {
const val preview = true
const val previewCommit = "b714abddae9f13e91bc53c5daac54aeae564cd2a"
val jvmTarget = JavaVersion.VERSION_16
val desktopJvmTarget = JavaVersion.VERSION_16
}

View File

@@ -1,9 +1,9 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile as KotlinJvmCompile
plugins {
kotlin("multiplatform")
kotlin("kapt")
id("com.android.library")
id("com.google.devtools.ksp")
id("com.codingfeline.buildkonfig")
id("org.jmailen.kotlinter")
}
group = "ca.gosyer"
@@ -15,7 +15,13 @@ repositories {
kotlin {
android()
jvm("desktop")
jvm("desktop") {
compilations {
all {
kotlinOptions.jvmTarget = Config.desktopJvmTarget.toString()
}
}
}
sourceSets {
all {
@@ -27,20 +33,23 @@ kotlin {
}
}
val commonMain by getting {
kotlin.srcDir("build/generated/ksp/commonMain/kotlin")
dependencies {
api(kotlin("stdlib-common"))
api(libs.coroutinesCore)
api(libs.json)
api(libs.toothpickKsp)
api(libs.kotlinInjectRuntime)
api(libs.ktorCore)
api(libs.ktorSerialization)
api(libs.okio)
api(libs.ktlogging)
api(libs.multiplatformSettingsCore)
api(libs.multiplatformSettingsCoroutines)
api(libs.multiplatformSettingsSerialization)
}
}
val commonTest by getting {
kotlin.srcDir("build/generated/ksp/commonTest/kotlin")
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
@@ -49,43 +58,36 @@ kotlin {
val desktopMain by getting {
kotlin.srcDir("src/jvmMain/kotlin")
kotlin.srcDir("build/generated/ksp/desktopMain/kotlin")
dependencies {
api(kotlin("stdlib-jdk8"))
api(libs.appDirs)
}
}
val desktopTest by getting {
kotlin.srcDir("src/jvmTest/kotlin")
kotlin.srcDir("build/generated/ksp/desktopTest/kotlin")
}
val androidMain by getting {
kotlin.srcDir("src/jvmMain/kotlin")
kotlin.srcDir("build/generated/ksp/androidMain/kotlin")
dependencies {
api(kotlin("stdlib-jdk8"))
}
}
val androidTest by getting {
kotlin.srcDir("src/jvmTest/kotlin")
kotlin.srcDir("build/generated/ksp/androidTest/kotlin")
}
}
}
dependencies {
add("kapt", libs.toothpickCompiler)
add("kspDesktop", libs.kotlinInjectCompiler)
add("kspAndroid", libs.kotlinInjectCompiler)
}
tasks {
withType<KotlinJvmCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
}
}
buildkonfig {
packageName = "ca.gosyer.core.build"
}
android {
compileSdk = 31
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 21
targetSdk = 31
}
}

View File

@@ -9,6 +9,9 @@ package ca.gosyer.core.prefs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
/**
* A wrapper around application preferences without knowing implementation details. Instances of
@@ -57,3 +60,10 @@ interface Preference<T> {
*/
fun stateIn(scope: CoroutineScope): StateFlow<T>
}
fun <T> Preference<T>.getAsFlow(action: (suspend (T) -> Unit)? = null): Flow<T> {
val flow = merge(flowOf(get()), changes())
return if (action != null) {
flow.onEach(action = action)
} else flow
}

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.core.io
import ca.gosyer.core.build.BuildKonfig
import mu.KotlinLogging
import net.harawata.appdirs.AppDirsFactory
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath
private val logger = KotlinLogging.logger {}
val userDataDir: Path by lazy {
AppDirsFactory.getInstance().getUserDataDir(BuildKonfig.NAME, null, null).toPath().also {
if (!FileSystem.SYSTEM.exists(it)) {
logger.info("Attempted to create app data dir, result: {}", FileSystem.SYSTEM.createDirectories(it))
}
}
}

View File

@@ -4,7 +4,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.util.system
package ca.gosyer.core.logging
import mu.KLogger
import mu.KotlinLogging

View File

@@ -6,25 +6,8 @@
package ca.gosyer.core.di
import toothpick.Scope
import toothpick.ktp.KTP
import me.tatarka.inject.annotations.Scope
/**
* The global scope for dependency injection that will provide all the application level components.
*/
object AppScope : Scope by KTP.openRootScope() {
/**
* Returns a new subscope inheriting the root scope.
*/
fun subscope(any: Any): Scope {
return openSubScope(any)
}
/**
* Returns an instance of [T] from the root scope.
*/
inline fun <reified T> getInstance(): T {
return getInstance(T::class.java)
}
}
@Scope
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER)
annotation class AppScope

View File

@@ -1,17 +0,0 @@
/*
* 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.core.di
import toothpick.Scope
import javax.inject.Provider
class GenericsProvider<T>(private val cls: Class<T>, val scope: Scope = AppScope) : Provider<T> {
override fun get(): T {
return scope.getInstance(cls)
}
}

View File

@@ -1,16 +0,0 @@
/*
* 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.core.di
import toothpick.config.Module
/**
* Binds the given [instance] to its class.
*/
inline fun <reified B> Module.bindInstance(instance: B) {
bind(B::class.java).toInstance(instance)
}

90
data/build.gradle.kts Normal file
View File

@@ -0,0 +1,90 @@
plugins {
kotlin("multiplatform")
id("com.google.devtools.ksp")
kotlin("plugin.serialization")
id("com.android.library")
id("com.codingfeline.buildkonfig")
id("org.jmailen.kotlinter")
}
kotlin {
android()
jvm("desktop") {
compilations {
all {
kotlinOptions.jvmTarget = Config.desktopJvmTarget.toString()
}
}
}
sourceSets {
all {
languageSettings {
optIn("kotlin.RequiresOptIn")
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
}
}
val commonMain by getting {
kotlin.srcDir("build/generated/ksp/commonMain/kotlin")
dependencies {
api(kotlin("stdlib-common"))
api(libs.coroutinesCore)
api(libs.json)
api(libs.kotlinInjectRuntime)
api(libs.ktorCore)
api(libs.ktorSerialization)
api(libs.ktorAuth)
api(libs.ktorLogging)
api(libs.ktorWebsockets)
api(libs.ktorOkHttp)
api(libs.okio)
api(project(":core"))
api(project(":i18n"))
}
}
val commonTest by getting {
kotlin.srcDir("build/generated/ksp/commonTest/kotlin")
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val desktopMain by getting {
kotlin.srcDir("src/jvmMain/kotlin")
kotlin.srcDir("build/generated/ksp/desktopMain/kotlin")
dependencies {
api(kotlin("stdlib-jdk8"))
}
}
val desktopTest by getting {
kotlin.srcDir("src/jvmTest/kotlin")
kotlin.srcDir("build/generated/ksp/desktopTest/kotlin")
}
val androidMain by getting {
kotlin.srcDir("src/jvmMain/kotlin")
kotlin.srcDir("build/generated/ksp/androidMain/kotlin")
dependencies {
api(kotlin("stdlib-jdk8"))
}
}
val androidTest by getting {
kotlin.srcDir("src/jvmTest/kotlin")
kotlin.srcDir("build/generated/ksp/androidTest/kotlin")
}
}
}
dependencies {
add("kspDesktop", libs.kotlinInjectCompiler)
add("kspAndroid", libs.kotlinInjectCompiler)
}
tasks {
}
buildkonfig {
packageName = "ca.gosyer.data.build"
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="ca.gosyer.data"/>

View File

@@ -0,0 +1,99 @@
/*
* 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.data
import ca.gosyer.core.di.AppScope
import ca.gosyer.core.prefs.PreferenceStoreFactory
import ca.gosyer.data.catalog.CatalogPreferences
import ca.gosyer.data.download.DownloadService
import ca.gosyer.data.extension.ExtensionPreferences
import ca.gosyer.data.library.LibraryPreferences
import ca.gosyer.data.library.LibraryUpdateService
import ca.gosyer.data.migration.MigrationPreferences
import ca.gosyer.data.migration.Migrations
import ca.gosyer.data.reader.ReaderPreferences
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.HttpProvider
import ca.gosyer.data.server.ServerHostPreferences
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.ServerService
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.data.update.UpdatePreferences
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Provides
@AppScope
@Component
abstract class DataComponent {
private val preferenceFactory = PreferenceStoreFactory()
protected abstract val httpProvider: HttpProvider
abstract val serverService: ServerService
abstract val downloadService: DownloadService
abstract val libraryUpdateService: LibraryUpdateService
abstract val migrations: Migrations
abstract val updateChecker: UpdateChecker
@get:AppScope
@get:Provides
val serverPreferences: ServerPreferences
get() = ServerPreferences(preferenceFactory.create("server"))
@get:AppScope
@get:Provides
val serverHostPreferences: ServerHostPreferences
get() = ServerHostPreferences(preferenceFactory.create("host"))
@get:AppScope
@get:Provides
val extensionPreferences: ExtensionPreferences
get() = ExtensionPreferences(preferenceFactory.create("extension"))
@get:AppScope
@get:Provides
val catalogPreferences: CatalogPreferences
get() = CatalogPreferences(preferenceFactory.create("catalog"))
@get:AppScope
@get:Provides
val libraryPreferences: LibraryPreferences
get() = LibraryPreferences(preferenceFactory.create("library"))
@get:AppScope
@get:Provides
val readerPreferences: ReaderPreferences
get() = ReaderPreferences(preferenceFactory.create("reader")) { name ->
preferenceFactory.create("reader", name)
}
@get:AppScope
@get:Provides
val uiPreferences: UiPreferences
get() = UiPreferences(preferenceFactory.create("ui"))
@get:AppScope
@get:Provides
val migrationPreferences: MigrationPreferences
get() = MigrationPreferences(preferenceFactory.create("migration"))
@get:AppScope
@get:Provides
val updatePreferences: UpdatePreferences
get() = UpdatePreferences(preferenceFactory.create("update"))
@get:AppScope
@get:Provides
val http: Http
get() = httpProvider.get(serverPreferences)
companion object
}

View File

@@ -4,13 +4,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.service
package ca.gosyer.data.base
import ca.gosyer.build.BuildConfig
import ca.gosyer.core.lang.throwIfCancellation
import ca.gosyer.core.logging.CKLogger
import ca.gosyer.data.build.BuildKonfig
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.util.system.CKLogger
import io.ktor.client.features.websocket.ws
import io.ktor.http.cio.websocket.Frame
import kotlinx.coroutines.CancellationException
@@ -33,7 +33,7 @@ abstract class WebsocketService(
protected val client: Http
) {
protected val json = Json {
ignoreUnknownKeys = !BuildConfig.DEBUG
ignoreUnknownKeys = !BuildKonfig.DEBUG
}
private val _status = MutableStateFlow(Status.STARTING)
val status = _status.asStateFlow()

View File

@@ -6,14 +6,14 @@
package ca.gosyer.data.download
import ca.gosyer.core.service.WebsocketService
import ca.gosyer.core.logging.CKLogger
import ca.gosyer.data.base.WebsocketService
import ca.gosyer.data.download.model.DownloadChapter
import ca.gosyer.data.download.model.DownloadStatus
import ca.gosyer.data.download.model.DownloaderStatus
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.requests.downloadsQuery
import ca.gosyer.util.system.CKLogger
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.readText
import kotlinx.coroutines.DelicateCoroutinesApi
@@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.decodeFromString
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
@OptIn(DelicateCoroutinesApi::class)
class DownloadService @Inject constructor(

View File

@@ -6,17 +6,17 @@
package ca.gosyer.data.library
import ca.gosyer.core.service.WebsocketService
import ca.gosyer.core.logging.CKLogger
import ca.gosyer.data.base.WebsocketService
import ca.gosyer.data.library.model.UpdateStatus
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.requests.updatesQuery
import ca.gosyer.util.system.CKLogger
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.readText
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.serialization.decodeFromString
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
@OptIn(DelicateCoroutinesApi::class)
class LibraryUpdateService @Inject constructor(

View File

@@ -6,9 +6,9 @@
package ca.gosyer.data.migration
import ca.gosyer.build.BuildConfig
import ca.gosyer.data.build.BuildKonfig
import ca.gosyer.data.reader.ReaderPreferences
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class Migrations @Inject constructor(
private val migrationPreferences: MigrationPreferences,
@@ -21,7 +21,7 @@ class Migrations @Inject constructor(
readerPreferences.modes().get().forEach {
readerPreferences.getMode(it).direction().delete()
}
migrationPreferences.version().set(BuildConfig.MIGRATION_CODE)
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
return
}
}

View File

@@ -6,7 +6,7 @@
package ca.gosyer.data.reader
import ca.gosyer.util.system.getAsFlow
import ca.gosyer.core.prefs.getAsFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow

View File

@@ -6,7 +6,7 @@
package ca.gosyer.data.server
import ca.gosyer.build.BuildConfig
import ca.gosyer.data.build.BuildKonfig
import ca.gosyer.data.server.model.Auth
import ca.gosyer.data.server.model.Proxy
import io.ktor.client.HttpClient
@@ -24,16 +24,13 @@ import io.ktor.client.features.logging.Logging
import io.ktor.client.features.websocket.WebSockets
import io.ktor.http.URLBuilder
import kotlinx.serialization.json.Json
import javax.inject.Inject
import javax.inject.Provider
import me.tatarka.inject.annotations.Inject
import io.ktor.client.features.auth.Auth as AuthFeature
typealias Http = HttpClient
internal class HttpProvider @Inject constructor(
private val serverPreferences: ServerPreferences
) : Provider<Http> {
override fun get(): Http {
class HttpProvider @Inject constructor() {
fun get(serverPreferences: ServerPreferences): Http {
return HttpClient(OkHttp) {
engine {
proxy = when (serverPreferences.proxy().get()) {
@@ -85,7 +82,7 @@ internal class HttpProvider @Inject constructor(
}
install(WebSockets)
install(Logging) {
level = if (BuildConfig.DEBUG) {
level = if (BuildKonfig.DEBUG) {
LogLevel.HEADERS
} else {
LogLevel.INFO

View File

@@ -6,10 +6,11 @@
package ca.gosyer.data.server
import ca.gosyer.build.BuildConfig
import ca.gosyer.core.io.copyTo
import ca.gosyer.core.io.userDataDir
import ca.gosyer.core.lang.withIOContext
import ca.gosyer.util.system.CKLogger
import ca.gosyer.util.system.userDataDir
import ca.gosyer.core.logging.CKLogger
import ca.gosyer.data.build.BuildKonfig
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
@@ -20,23 +21,22 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject
import mu.KotlinLogging
import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath
import okio.buffer
import okio.source
import java.io.File.pathSeparatorChar
import java.io.IOException
import java.io.Reader
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarInputStream
import javax.inject.Inject
import kotlin.concurrent.thread
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.createDirectories
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.isExecutable
import kotlin.io.path.name
import kotlin.io.path.outputStream
@OptIn(DelicateCoroutinesApi::class)
class ServerService @Inject constructor(
@@ -64,17 +64,14 @@ class ServerService @Inject constructor(
}
@Throws(IOException::class)
private fun copyJar(jarFile: Path) {
javaClass.getResourceAsStream("/Tachidesk.jar")?.buffered()?.use { input ->
jarFile.outputStream().use { output ->
input.copyTo(output)
}
}
private suspend fun copyJar(jarFile: Path) {
javaClass.getResourceAsStream("/Tachidesk.jar")?.source()
?.copyTo(FileSystem.SYSTEM.sink(jarFile).buffer())
}
private fun getJavaFromPath(javaPath: Path): String? {
val javaExeFile = javaPath.resolve("java.exe")
val javaUnixFile = javaPath.resolve("java")
val javaExeFile = javaPath.resolve("java.exe").toNioPath()
val javaUnixFile = javaPath.resolve("java").toNioPath()
return when {
javaExeFile.exists() && javaExeFile.isExecutable() -> javaExeFile.absolutePathString()
javaUnixFile.exists() && javaUnixFile.isExecutable() -> javaUnixFile.absolutePathString()
@@ -83,7 +80,7 @@ class ServerService @Inject constructor(
}
private fun getRuntimeJava(): String? {
return System.getProperty("java.home")?.let { getJavaFromPath(Path(it).resolve("bin")) }
return System.getProperty("java.home")?.let { getJavaFromPath(it.toPath().resolve("bin")) }
}
private fun getPossibleJava(): String? {
@@ -91,8 +88,8 @@ class ServerService @Inject constructor(
.orEmpty()
.asSequence()
.mapNotNull {
val file = Path(it)
if (file.absolutePathString().contains("java") || file.absolutePathString().contains("jdk")) {
val file = it.toPath()
if (file.toString().contains("java") || file.toString().contains("jdk")) {
if (file.name.equals("bin", true)) {
file
} else file.resolve("bin")
@@ -126,25 +123,25 @@ class ServerService @Inject constructor(
}
}
GlobalScope.launch(handler) {
val jarFile = userDataDir.also { it.createDirectories() }.resolve("Tachidesk.jar")
if (!jarFile.exists()) {
val jarFile = userDataDir / "Tachidesk.jar"
if (!FileSystem.SYSTEM.exists(jarFile)) {
info { "Copying server to resources" }
withIOContext { copyJar(jarFile) }
} else {
try {
val jarVersion = withIOContext {
JarInputStream(jarFile.inputStream()).use { jar ->
JarInputStream(FileSystem.SYSTEM.source(jarFile).buffer().inputStream()).use { jar ->
jar.manifest?.mainAttributes?.getValue(Attributes.Name.IMPLEMENTATION_VERSION)?.toIntOrNull()
}
}
if (jarVersion != BuildConfig.SERVER_CODE) {
if (jarVersion != BuildKonfig.SERVER_CODE) {
info { "Updating server file from resources" }
withIOContext { copyJar(jarFile) }
}
} catch (e: IOException) {
error(e) {
"Error accessing server jar, cannot update server, ${BuildConfig.NAME} may not work properly"
"Error accessing server jar, cannot update server, ${BuildKonfig.NAME} may not work properly"
}
}
}
@@ -156,7 +153,7 @@ class ServerService @Inject constructor(
withIOContext {
val reader: Reader
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.absolutePathString())
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.toString())
.redirectErrorStream(true)
.start()
.also {

View File

@@ -21,9 +21,10 @@ import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import java.nio.file.Path
import javax.inject.Inject
import kotlin.io.path.readBytes
import me.tatarka.inject.annotations.Inject
import okio.FileSystem
import okio.Path
import okio.buffer
class BackupInteractionHandler @Inject constructor(
client: Http,
@@ -35,7 +36,7 @@ class BackupInteractionHandler @Inject constructor(
serverUrl + backupFileImportRequest(),
formData = formData {
append(
"backup.proto.gz", file.readBytes(),
"backup.proto.gz", FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
@@ -51,7 +52,7 @@ class BackupInteractionHandler @Inject constructor(
serverUrl + validateBackupFileRequest(),
formData = formData {
append(
"backup.proto.gz", file.readBytes(),
"backup.proto.gz", FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")

View File

@@ -26,7 +26,7 @@ import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class CategoryInteractionHandler @Inject constructor(
client: Http,

View File

@@ -19,7 +19,6 @@ import ca.gosyer.data.server.requests.queueDownloadChapterRequest
import ca.gosyer.data.server.requests.stopDownloadingChapterRequest
import ca.gosyer.data.server.requests.updateChapterMetaRequest
import ca.gosyer.data.server.requests.updateChapterRequest
import ca.gosyer.util.compose.imageFromUrl
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.delete
import io.ktor.client.request.forms.submitForm
@@ -28,7 +27,8 @@ import io.ktor.client.request.parameter
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import javax.inject.Inject
import io.ktor.utils.io.ByteReadChannel
import me.tatarka.inject.annotations.Inject
class ChapterInteractionHandler @Inject constructor(
client: Http,
@@ -123,8 +123,7 @@ class ChapterInteractionHandler @Inject constructor(
)
suspend fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = withIOContext {
imageFromUrl(
client,
client.get<ByteReadChannel>(
serverUrl + getPageQuery(mangaId, chapterIndex, pageNum),
block
)

View File

@@ -14,7 +14,7 @@ import ca.gosyer.data.server.requests.downloadsStartRequest
import ca.gosyer.data.server.requests.downloadsStopRequest
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class DownloadInteractionHandler @Inject constructor(
client: Http,

View File

@@ -15,11 +15,11 @@ import ca.gosyer.data.server.requests.apkInstallQuery
import ca.gosyer.data.server.requests.apkUninstallQuery
import ca.gosyer.data.server.requests.apkUpdateQuery
import ca.gosyer.data.server.requests.extensionListQuery
import ca.gosyer.util.compose.imageFromUrl
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import javax.inject.Inject
import io.ktor.utils.io.ByteReadChannel
import me.tatarka.inject.annotations.Inject
class ExtensionInteractionHandler @Inject constructor(
client: Http,
@@ -51,8 +51,7 @@ class ExtensionInteractionHandler @Inject constructor(
}
suspend fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit) = withIOContext {
imageFromUrl(
client,
client.get<ByteReadChannel>(
serverUrl + apkIconQuery(extension.apkName),
block
)

View File

@@ -15,7 +15,7 @@ import ca.gosyer.data.server.requests.removeMangaFromLibraryRequest
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class LibraryInteractionHandler @Inject constructor(
client: Http,

View File

@@ -13,7 +13,6 @@ import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.requests.mangaQuery
import ca.gosyer.data.server.requests.mangaThumbnailQuery
import ca.gosyer.data.server.requests.updateMangaMetaRequest
import ca.gosyer.util.compose.imageFromUrl
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.get
@@ -21,7 +20,8 @@ import io.ktor.client.request.parameter
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpMethod
import io.ktor.http.Parameters
import javax.inject.Inject
import io.ktor.utils.io.ByteReadChannel
import me.tatarka.inject.annotations.Inject
class MangaInteractionHandler @Inject constructor(
client: Http,
@@ -43,8 +43,7 @@ class MangaInteractionHandler @Inject constructor(
suspend fun getManga(manga: Manga, refresh: Boolean = false) = getManga(manga.id, refresh)
suspend fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit) = withIOContext {
imageFromUrl(
client,
client.get<ByteReadChannel>(
serverUrl + mangaThumbnailQuery(mangaId),
block
)

View File

@@ -33,7 +33,7 @@ import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class SourceInteractionHandler @Inject constructor(
client: Http,

View File

@@ -18,7 +18,7 @@ import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.statement.HttpResponse
import io.ktor.http.Parameters
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class UpdatesInteractionHandler @Inject constructor(
client: Http,

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.data.ui.model
import kotlinx.serialization.Serializable
@Serializable
data class WindowSettings(
val x: Int? = null,
val y: Int? = null,
val width: Int? = null,
val height: Int? = null,
val maximized: Boolean? = null,
val fullscreen: Boolean? = null
)

View File

@@ -6,16 +6,16 @@
package ca.gosyer.data.update
import ca.gosyer.build.BuildConfig
import ca.gosyer.core.lang.launch
import ca.gosyer.core.lang.withIOContext
import ca.gosyer.data.build.BuildKonfig
import ca.gosyer.data.server.Http
import ca.gosyer.data.update.model.GithubRelease
import io.ktor.client.request.get
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import javax.inject.Inject
import me.tatarka.inject.annotations.Inject
class UpdateChecker @Inject constructor(
private val updatePreferences: UpdatePreferences,
@@ -42,19 +42,19 @@ class UpdateChecker @Inject constructor(
// Removes prefixes like "r" or "v"
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
return if (BuildConfig.IS_PREVIEW) {
return if (BuildKonfig.IS_PREVIEW) {
// Preview builds: based on releases in "Suwayomi/Tachidesk-JUI-preview" repo
// tagged as something like "r123"
newVersion.toInt() > BuildConfig.PREVIEW_BUILD
newVersion.toInt() > BuildKonfig.PREVIEW_BUILD
} else {
// Release builds: based on releases in "Suwayomi/Tachidesk-JUI" repo
// tagged as something like "v1.1.2"
newVersion != BuildConfig.VERSION
newVersion != BuildKonfig.VERSION
}
}
companion object {
private val GITHUB_REPO = if (BuildConfig.IS_PREVIEW) {
private val GITHUB_REPO = if (BuildKonfig.IS_PREVIEW) {
"Suwayomi/Tachidesk-JUI-preview"
} else {
"Suwayomi/Tachidesk-JUI"

Some files were not shown because too many files have changed in this diff Show More