This commit is contained in:
Syer10
2023-12-01 22:44:25 -05:00
parent 4afdd217b7
commit 996dca6cee
207 changed files with 1164 additions and 983 deletions

View File

@@ -20,7 +20,9 @@ import kotlinx.coroutines.flow.launchIn
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
import java.util.Locale import java.util.Locale
class App : Application(), DefaultLifecycleObserver { class App :
Application(),
DefaultLifecycleObserver {
override fun onCreate() { override fun onCreate() {
super<Application>.onCreate() super<Application>.onCreate()

View File

@@ -22,7 +22,10 @@ abstract class AppComponent(
@get:AppScope @get:AppScope
@get:Provides @get:Provides
val context: Context, val context: Context,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent { ) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations abstract val appMigrations: AppMigrations
@get:AppScope @get:AppScope

View File

@@ -208,7 +208,10 @@ class AndroidDownloadService : Service() {
val title = downloadingChapter.manga.title.chop(15) val title = downloadingChapter.manga.title.chop(15)
val quotedTitle = Pattern.quote(title) val quotedTitle = Pattern.quote(title)
val chapter = downloadingChapter.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "") val chapter = downloadingChapter.chapter.name.replaceFirst(
"$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE),
"",
)
setContentTitle("$title - $chapter".chop(30)) setContentTitle("$title - $chapter".chop(30))
setContentText( setContentText(

View File

@@ -23,9 +23,12 @@ import ca.gosyer.jui.i18n.MR
import dev.icerock.moko.resources.desc.desc import dev.icerock.moko.resources.desc.desc
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class UpdateCheckWorker(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { class UpdateCheckWorker(
override suspend fun doWork(): Result { private val context: Context,
return try { workerParams: WorkerParameters,
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result =
try {
val update = AppComponent.getInstance(context.applicationContext) val update = AppComponent.getInstance(context.applicationContext)
.let { .let {
if (it.updatePreferences.enabled().get()) { if (it.updatePreferences.enabled().get()) {
@@ -48,7 +51,6 @@ class UpdateCheckWorker(private val context: Context, workerParams: WorkerParame
} catch (e: Exception) { } catch (e: Exception) {
Result.failure() Result.failure()
} }
}
companion object { companion object {
private const val TAG = "UpdateChecker" private const val TAG = "UpdateChecker"

View File

@@ -12,9 +12,11 @@ import me.tatarka.inject.annotations.Inject
actual class PreferenceStoreFactory actual class PreferenceStoreFactory
@Inject @Inject
constructor(private val context: Context) { constructor(
actual fun create(vararg names: String): PreferenceStore { private val context: Context,
return StandardPreferenceStore( ) {
actual fun create(vararg names: String): PreferenceStore =
StandardPreferenceStore(
SharedPreferencesSettings( SharedPreferencesSettings(
context.getSharedPreferences( context.getSharedPreferences(
names.joinToString(separator = "_"), names.joinToString(separator = "_"),
@@ -22,5 +24,4 @@ actual class PreferenceStoreFactory
), ),
), ),
) )
}
} }

View File

@@ -35,6 +35,4 @@ suspend fun Source.copyTo(sink: BufferedSink) {
} }
} }
fun ByteArray.source(): BufferedSource { fun ByteArray.source(): BufferedSource = Buffer().write(this)
return Buffer().write(this)
}

View File

@@ -54,7 +54,9 @@ suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T): T = withCo
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block) suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)
fun Throwable.throwIfCancellation() { if (this is CancellationException) throw this } fun Throwable.throwIfCancellation() {
if (this is CancellationException) throw this
}
fun <T> Result<T>.throwIfCancellation(): Result<T> { fun <T> Result<T>.throwIfCancellation(): Result<T> {
if (isFailure) { if (isFailure) {

View File

@@ -73,7 +73,10 @@ internal open class ProcessChannel<T>(
override fun offer(element: T): Boolean = inChannel.trySend(element).isSuccess override fun offer(element: T): Boolean = inChannel.trySend(element).isSuccess
@Deprecated( @Deprecated(
"Deprecated in the favour of 'tryReceive'. Please note that the provided replacement does not rethrow channel's close cause as 'poll' did, for the precise replacement please refer to the 'poll' documentation", "Deprecated in the favour of 'tryReceive'. " +
"Please note that the provided replacement does not rethrow " +
"channel's close cause as 'poll' did, for the precise replacement " +
"please refer to the 'poll' documentation",
replaceWith = ReplaceWith("tryReceive().getOrNull()"), replaceWith = ReplaceWith("tryReceive().getOrNull()"),
level = DeprecationLevel.ERROR, level = DeprecationLevel.ERROR,
) )
@@ -82,6 +85,7 @@ internal open class ProcessChannel<T>(
override suspend fun receive(): T = outChannel.receive() override suspend fun receive(): T = outChannel.receive()
override suspend fun send(element: T) = inChannel.send(element) override suspend fun send(element: T) = inChannel.send(element)
override val onReceiveCatching: SelectClause1<ChannelResult<T>> override val onReceiveCatching: SelectClause1<ChannelResult<T>>
get() = TODO("not implemented") get() = TODO("not implemented")
@@ -105,16 +109,16 @@ internal class PriorityChannelImpl<T>(
scope: CoroutineScope, scope: CoroutineScope,
comparator: Comparator<T>, comparator: Comparator<T>,
) : ProcessChannel<T>( ) : ProcessChannel<T>(
// why a rendezvous channel should be the input channel? // why a rendezvous channel should be the input channel?
// because we buffer and sort the messages in the co-routine // because we buffer and sort the messages in the co-routine
// that is where the capacity constraint is enforced // that is where the capacity constraint is enforced
// and the buffer we keep sorted, the input channel we can't // and the buffer we keep sorted, the input channel we can't
inChannel = Channel(Channel.RENDEZVOUS), inChannel = Channel(Channel.RENDEZVOUS),
// output channel is rendezvous channel because we may still // output channel is rendezvous channel because we may still
// get higher priority input meanwhile and we will send that // get higher priority input meanwhile and we will send that
// when output consumer is ready to take it // when output consumer is ready to take it
outChannel = Channel(Channel.RENDEZVOUS), outChannel = Channel(Channel.RENDEZVOUS),
) { ) {
private val buffer = PriorityQueue(comparator) private val buffer = PriorityQueue(comparator)
private fun PriorityQueue<T>.isNotFull() = this.size < maxCapacity private fun PriorityQueue<T>.isNotFull() = this.size < maxCapacity

View File

@@ -13,18 +13,16 @@ package ca.gosyer.jui.core.lang
fun String.chop( fun String.chop(
count: Int, count: Int,
replacement: String = "", replacement: String = "",
): String { ): String =
return if (length > count) { if (length > count) {
take(count - replacement.length) + replacement take(count - replacement.length) + replacement
} else { } else {
this this
} }
}
fun String.addSuffix(char: Char): String { fun String.addSuffix(char: Char): String =
return if (endsWith(char)) { if (endsWith(char)) {
this this
} else { } else {
this + char this + char
} }
}

View File

@@ -22,9 +22,7 @@ class LazyPreferenceStore(
override fun getString( override fun getString(
key: String, key: String,
defaultValue: String, defaultValue: String,
): Preference<String> { ): Preference<String> = lazyStore.value.getString(key, defaultValue)
return lazyStore.value.getString(key, defaultValue)
}
/** /**
* Returns a [Long] preference for this [key]. * Returns a [Long] preference for this [key].
@@ -32,9 +30,7 @@ class LazyPreferenceStore(
override fun getLong( override fun getLong(
key: String, key: String,
defaultValue: Long, defaultValue: Long,
): Preference<Long> { ): Preference<Long> = lazyStore.value.getLong(key, defaultValue)
return lazyStore.value.getLong(key, defaultValue)
}
/** /**
* Returns an [Int] preference for this [key]. * Returns an [Int] preference for this [key].
@@ -42,9 +38,7 @@ class LazyPreferenceStore(
override fun getInt( override fun getInt(
key: String, key: String,
defaultValue: Int, defaultValue: Int,
): Preference<Int> { ): Preference<Int> = lazyStore.value.getInt(key, defaultValue)
return lazyStore.value.getInt(key, defaultValue)
}
/** /**
* Returns a [Float] preference for this [key]. * Returns a [Float] preference for this [key].
@@ -52,9 +46,7 @@ class LazyPreferenceStore(
override fun getFloat( override fun getFloat(
key: String, key: String,
defaultValue: Float, defaultValue: Float,
): Preference<Float> { ): Preference<Float> = lazyStore.value.getFloat(key, defaultValue)
return lazyStore.value.getFloat(key, defaultValue)
}
/** /**
* Returns a [Boolean] preference for this [key]. * Returns a [Boolean] preference for this [key].
@@ -62,9 +54,7 @@ class LazyPreferenceStore(
override fun getBoolean( override fun getBoolean(
key: String, key: String,
defaultValue: Boolean, defaultValue: Boolean,
): Preference<Boolean> { ): Preference<Boolean> = lazyStore.value.getBoolean(key, defaultValue)
return lazyStore.value.getBoolean(key, defaultValue)
}
/** /**
* Returns a [Set<String>] preference for this [key]. * Returns a [Set<String>] preference for this [key].
@@ -72,9 +62,7 @@ class LazyPreferenceStore(
override fun getStringSet( override fun getStringSet(
key: String, key: String,
defaultValue: Set<String>, defaultValue: Set<String>,
): Preference<Set<String>> { ): Preference<Set<String>> = lazyStore.value.getStringSet(key, defaultValue)
return lazyStore.value.getStringSet(key, defaultValue)
}
/** /**
* Returns preference of type [T] for this [key]. The [serializer] and [deserializer] function * Returns preference of type [T] for this [key]. The [serializer] and [deserializer] function
@@ -85,16 +73,12 @@ class LazyPreferenceStore(
defaultValue: T, defaultValue: T,
serializer: (T) -> String, serializer: (T) -> String,
deserializer: (String) -> T, deserializer: (String) -> T,
): Preference<T> { ): Preference<T> = lazyStore.value.getObject(key, defaultValue, serializer, deserializer)
return lazyStore.value.getObject(key, defaultValue, serializer, deserializer)
}
override fun <T> getJsonObject( override fun <T> getJsonObject(
key: String, key: String,
defaultValue: T, defaultValue: T,
serializer: KSerializer<T>, serializer: KSerializer<T>,
serializersModule: SerializersModule, serializersModule: SerializersModule,
): Preference<T> { ): Preference<T> = lazyStore.value.getJsonObject(key, defaultValue, serializer)
return lazyStore.value.getJsonObject(key, defaultValue, serializer)
}
} }

View File

@@ -91,8 +91,8 @@ interface PreferenceStore {
inline fun <reified T : Enum<T>> PreferenceStore.getEnum( inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
key: String, key: String,
defaultValue: T, defaultValue: T,
): Preference<T> { ): Preference<T> =
return getObject( getObject(
key, key,
defaultValue, defaultValue,
{ it.name }, { it.name },
@@ -104,4 +104,3 @@ inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
} }
}, },
) )
}

View File

@@ -24,20 +24,17 @@ internal class StandardPreference<T>(
/** /**
* Returns the key of this preference. * Returns the key of this preference.
*/ */
override fun key(): String { override fun key(): String = key
return key
}
/** /**
* Returns the current value of this preference. * Returns the current value of this preference.
*/ */
override fun get(): T { override fun get(): T =
return if (isSet()) { if (isSet()) {
adapter.get(key, preferences) adapter.get(key, preferences)
} else { } else {
defaultValue defaultValue
} }
}
/** /**
* Sets a new [value] for this preference. * Sets a new [value] for this preference.
@@ -49,9 +46,7 @@ internal class StandardPreference<T>(
/** /**
* Returns whether there's an existing entry for this preference. * Returns whether there's an existing entry for this preference.
*/ */
override fun isSet(): Boolean { override fun isSet(): Boolean = adapter.isSet(preferences.keys, key)
return adapter.isSet(preferences.keys, key)
}
/** /**
* Deletes the entry of this preference. * Deletes the entry of this preference.
@@ -63,27 +58,22 @@ internal class StandardPreference<T>(
/** /**
* Returns the default value of this preference * Returns the default value of this preference
*/ */
override fun defaultValue(): T { override fun defaultValue(): T = defaultValue
return defaultValue
}
/** /**
* Returns a cold [Flow] of this preference to receive updates when its value changes. * Returns a cold [Flow] of this preference to receive updates when its value changes.
*/ */
override fun changes(): Flow<T> { override fun changes(): Flow<T> =
return callbackFlow { callbackFlow {
val listener = adapter.addListener(key, preferences) { val listener = adapter.addListener(key, preferences) {
trySend(get()) trySend(get())
} }
awaitClose { listener.deactivate() } awaitClose { listener.deactivate() }
} }
}
/** /**
* Returns a hot [StateFlow] of this preference bound to the given [scope], allowing to read the * Returns a hot [StateFlow] of this preference bound to the given [scope], allowing to read the
* current value and receive preference updates. * current value and receive preference updates.
*/ */
override fun stateIn(scope: CoroutineScope): StateFlow<T> { override fun stateIn(scope: CoroutineScope): StateFlow<T> = changes().stateIn(scope, SharingStarted.Eagerly, get())
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
} }

View File

@@ -10,7 +10,9 @@ import com.russhwolf.settings.ObservableSettings
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
class StandardPreferenceStore(private val preferences: ObservableSettings) : PreferenceStore { class StandardPreferenceStore(
private val preferences: ObservableSettings,
) : PreferenceStore {
/** /**
* Returns an [String] preference for this [key]. * Returns an [String] preference for this [key].
*/ */

View File

@@ -31,11 +31,12 @@ object ImageUtil {
return true return true
} }
private fun charByteArrayOf(vararg bytes: Int): ByteArray { private fun charByteArrayOf(vararg bytes: Int): ByteArray = ByteArray(bytes.size) { pos -> bytes[pos].toByte() }
return ByteArray(bytes.size) { pos -> bytes[pos].toByte() }
}
enum class ImageType(val mime: String, val extension: String) { enum class ImageType(
val mime: String,
val extension: String,
) {
JPG("image/jpeg", "jpg"), JPG("image/jpeg", "jpg"),
PNG("image/png", "png"), PNG("image/png", "png"),
GIF("image/gif", "gif"), GIF("image/gif", "gif"),

View File

@@ -16,11 +16,10 @@ actual class PreferenceStoreFactory
private val rootNode: Preferences = Preferences.userRoot() private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui") .node("ca/gosyer/tachideskjui")
actual fun create(vararg names: String): PreferenceStore { actual fun create(vararg names: String): PreferenceStore =
return StandardPreferenceStore( StandardPreferenceStore(
PreferencesSettings( PreferencesSettings(
rootNode.node(names.joinToString(separator = "/")), rootNode.node(names.joinToString(separator = "/")),
), ),
) )
}
} }

View File

@@ -13,11 +13,10 @@ import platform.Foundation.NSUserDefaults
actual class PreferenceStoreFactory actual class PreferenceStoreFactory
@Inject @Inject
constructor() { constructor() {
actual fun create(vararg names: String): PreferenceStore { actual fun create(vararg names: String): PreferenceStore =
return StandardPreferenceStore( StandardPreferenceStore(
NSUserDefaultsSettings( NSUserDefaultsSettings(
NSUserDefaults.standardUserDefaults, NSUserDefaults.standardUserDefaults,
), ),
) )
}
} }

View File

@@ -49,7 +49,5 @@ class FlowConverterFactory : Converter.Factory {
override fun suspendResponseConverter( override fun suspendResponseConverter(
typeData: TypeData, typeData: TypeData,
ktorfit: Ktorfit, ktorfit: Ktorfit,
): Converter.SuspendResponseConverter<HttpResponse, *>? { ): Converter.SuspendResponseConverter<HttpResponse, *>? = null
return null
}
} }

View File

@@ -20,7 +20,10 @@ import me.tatarka.inject.annotations.Provides
abstract class AppComponent( abstract class AppComponent(
@get:Provides @get:Provides
val context: ContextWrapper, val context: ContextWrapper,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent { ) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations abstract val appMigrations: AppMigrations
@get:AppScope @get:AppScope

View File

@@ -15,12 +15,12 @@ class Slf4jLogFactory : LogFactory {
override fun createKmLog( override fun createKmLog(
tag: String, tag: String,
className: String, className: String,
): KmLog { ): KmLog = Slf4jLog(tag)
return Slf4jLog(tag)
}
} }
class Slf4jLog(tag: String) : KmLog(tag) { class Slf4jLog(
tag: String,
) : KmLog(tag) {
private val logger: Logger = LoggerFactory.getLogger(tag) private val logger: Logger = LoggerFactory.getLogger(tag)
override fun verbose( override fun verbose(

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class ExportBackupFile class ExportBackupFile
@Inject @Inject
constructor(private val backupRepository: BackupRepository) { constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await( suspend fun await(
block: HttpRequestBuilder.() -> Unit = {}, block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ImportBackupFile class ImportBackupFile
@Inject @Inject
constructor(private val backupRepository: BackupRepository) { constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await( suspend fun await(
file: Path, file: Path,
block: HttpRequestBuilder.() -> Unit = {}, block: HttpRequestBuilder.() -> Unit = {},

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ValidateBackupFile class ValidateBackupFile
@Inject @Inject
constructor(private val backupRepository: BackupRepository) { constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await( suspend fun await(
file: Path, file: Path,
block: HttpRequestBuilder.() -> Unit = {}, block: HttpRequestBuilder.() -> Unit = {},

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class CreateCategory class CreateCategory
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
name: String, name: String,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class DeleteCategory class DeleteCategory
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
categoryId: Long, categoryId: Long,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetCategories class GetCategories
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
dropDefault: Boolean = false, dropDefault: Boolean = false,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetMangaCategories class GetMangaCategories
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class ModifyCategory class ModifyCategory
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
categoryId: Long, categoryId: Long,
name: String, name: String,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class ReorderCategory class ReorderCategory
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
to: Int, to: Int,
from: Int, from: Int,

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class UpdateCategoryMeta class UpdateCategoryMeta
@Inject @Inject
constructor(private val categoryRepository: CategoryRepository) { constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await( suspend fun await(
category: Category, category: Category,
example: Int = category.meta.example, example: Int = category.meta.example,

View File

@@ -17,7 +17,9 @@ import org.lighthousegames.logging.logging
class GetChapterPage class GetChapterPage
@Inject @Inject
constructor(private val chapterRepository: ChapterRepository) { constructor(
private val chapterRepository: ChapterRepository,
) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class BatchChapterDownload class BatchChapterDownload
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await( suspend fun await(
chapterIds: List<Long>, chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class ClearDownloadQueue class ClearDownloadQueue
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class QueueChapterDownload class QueueChapterDownload
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ReorderChapterDownload class ReorderChapterDownload
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class StartDownloading class StartDownloading
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class StopChapterDownload class StopChapterDownload
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class StopDownloading class StopDownloading
@Inject @Inject
constructor(private val downloadRepository: DownloadRepository) { constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -11,7 +11,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
@Stable @Stable
enum class DownloadState(val state: Int) { enum class DownloadState(
val state: Int,
) {
Queued(0), Queued(0),
Downloading(1), Downloading(1),
Finished(2), Finished(2),

View File

@@ -47,6 +47,7 @@ class DownloadService
.map { .map {
it.filter { it.mangaId == mangaId } it.filter { it.mangaId == mangaId }
} }
fun registerWatches(mangaIds: Set<Long>) = fun registerWatches(mangaIds: Set<Long>) =
downloadQueue downloadQueue
.map { .map {

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetExtensionList class GetExtensionList
@Inject @Inject
constructor(private val extensionRepository: ExtensionRepository) { constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class InstallExtension class InstallExtension
@Inject @Inject
constructor(private val extensionRepository: ExtensionRepository) { constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await( suspend fun await(
extension: Extension, extension: Extension,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class InstallExtensionFile class InstallExtensionFile
@Inject @Inject
constructor(private val extensionRepository: ExtensionRepository) { constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await( suspend fun await(
path: Path, path: Path,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UninstallExtension class UninstallExtension
@Inject @Inject
constructor(private val extensionRepository: ExtensionRepository) { constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await( suspend fun await(
extension: Extension, extension: Extension,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UpdateExtension class UpdateExtension
@Inject @Inject
constructor(private val extensionRepository: ExtensionRepository) { constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await( suspend fun await(
extension: Extension, extension: Extension,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -10,8 +10,12 @@ import androidx.compose.ui.text.intl.Locale
import ca.gosyer.jui.core.prefs.Preference import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore import ca.gosyer.jui.core.prefs.PreferenceStore
class ExtensionPreferences(private val preferenceStore: PreferenceStore) { class ExtensionPreferences(
fun languages(): Preference<Set<String>> { private val preferenceStore: PreferenceStore,
return preferenceStore.getStringSet("enabled_langs", setOfNotNull("all", "en", Locale.current.language)) ) {
} fun languages(): Preference<Set<String>> =
preferenceStore.getStringSet(
"enabled_langs",
setOfNotNull("all", "en", Locale.current.language),
)
} }

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetGlobalMeta class GetGlobalMeta
@Inject @Inject
constructor(private val globalRepository: GlobalRepository) { constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class UpdateGlobalMeta class UpdateGlobalMeta
@Inject @Inject
constructor(private val globalRepository: GlobalRepository) { constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await( suspend fun await(
globalMeta: GlobalMeta, globalMeta: GlobalMeta,
example: Int = globalMeta.example, example: Int = globalMeta.example,

View File

@@ -11,56 +11,52 @@ import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.library.model.FilterState import ca.gosyer.jui.domain.library.model.FilterState
import ca.gosyer.jui.domain.library.model.Sort import ca.gosyer.jui.domain.library.model.Sort
class LibraryPreferences(private val preferenceStore: PreferenceStore) { class LibraryPreferences(
fun showAllCategory(): Preference<Boolean> { private val preferenceStore: PreferenceStore,
return preferenceStore.getBoolean("show_all_category", false) ) {
} fun showAllCategory(): Preference<Boolean> = preferenceStore.getBoolean("show_all_category", false)
fun filterDownloaded(): Preference<FilterState> { fun filterDownloaded(): Preference<FilterState> =
return preferenceStore.getJsonObject("filter_downloaded", FilterState.IGNORED, FilterState.serializer()) preferenceStore.getJsonObject(
} "filter_downloaded",
FilterState.IGNORED,
FilterState.serializer(),
)
fun filterUnread(): Preference<FilterState> { fun filterUnread(): Preference<FilterState> =
return preferenceStore.getJsonObject("filter_unread", FilterState.IGNORED, FilterState.serializer()) preferenceStore.getJsonObject(
} "filter_unread",
FilterState.IGNORED,
FilterState.serializer(),
)
fun filterCompleted(): Preference<FilterState> { fun filterCompleted(): Preference<FilterState> =
return preferenceStore.getJsonObject("filter_completed", FilterState.IGNORED, FilterState.serializer()) preferenceStore.getJsonObject(
} "filter_completed",
FilterState.IGNORED,
FilterState.serializer(),
)
fun sortMode(): Preference<Sort> { fun sortMode(): Preference<Sort> = preferenceStore.getJsonObject("sort_mode", Sort.ALPHABETICAL, Sort.serializer())
return preferenceStore.getJsonObject("sort_mode", Sort.ALPHABETICAL, Sort.serializer())
}
fun sortAscending(): Preference<Boolean> { fun sortAscending(): Preference<Boolean> = preferenceStore.getBoolean("sort_ascending", true)
return preferenceStore.getBoolean("sort_ascending", true)
}
fun displayMode(): Preference<ca.gosyer.jui.domain.library.model.DisplayMode> { fun displayMode(): Preference<ca.gosyer.jui.domain.library.model.DisplayMode> =
return preferenceStore.getJsonObject("display_mode", ca.gosyer.jui.domain.library.model.DisplayMode.CompactGrid, ca.gosyer.jui.domain.library.model.DisplayMode.serializer()) preferenceStore.getJsonObject(
} "display_mode",
ca.gosyer.jui.domain.library.model.DisplayMode.CompactGrid,
ca.gosyer.jui.domain.library.model.DisplayMode.serializer(),
)
fun gridColumns(): Preference<Int> { fun gridColumns(): Preference<Int> = preferenceStore.getInt("grid_columns", 0)
return preferenceStore.getInt("grid_columns", 0)
}
fun gridSize(): Preference<Int> { fun gridSize(): Preference<Int> = preferenceStore.getInt("grid_size", 160)
return preferenceStore.getInt("grid_size", 160)
}
fun unreadBadge(): Preference<Boolean> { fun unreadBadge(): Preference<Boolean> = preferenceStore.getBoolean("unread_badge", true)
return preferenceStore.getBoolean("unread_badge", true)
}
fun downloadBadge(): Preference<Boolean> { fun downloadBadge(): Preference<Boolean> = preferenceStore.getBoolean("download_badge", false)
return preferenceStore.getBoolean("download_badge", false)
}
fun languageBadge(): Preference<Boolean> { fun languageBadge(): Preference<Boolean> = preferenceStore.getBoolean("language_badge", false)
return preferenceStore.getBoolean("language_badge", false)
}
fun localBadge(): Preference<Boolean> { fun localBadge(): Preference<Boolean> = preferenceStore.getBoolean("local_badge", false)
return preferenceStore.getBoolean("local_badge", false)
}
} }

View File

@@ -9,11 +9,10 @@ package ca.gosyer.jui.domain.migration.service
import ca.gosyer.jui.core.prefs.Preference import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore import ca.gosyer.jui.core.prefs.PreferenceStore
class MigrationPreferences(private val preferenceStore: PreferenceStore) { class MigrationPreferences(
fun version(): Preference<Int> { private val preferenceStore: PreferenceStore,
return preferenceStore.getInt("version", 0) ) {
} fun version(): Preference<Int> = preferenceStore.getInt("version", 0)
fun appVersion(): Preference<Int> {
return preferenceStore.getInt("app_version", 0) fun appVersion(): Preference<Int> = preferenceStore.getInt("app_version", 0)
}
} }

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
@Stable @Stable
enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) { enum class TappingInvertMode(
val shouldInvertHorizontal: Boolean = false,
val shouldInvertVertical: Boolean = false,
) {
NONE, NONE,
HORIZONTAL(shouldInvertHorizontal = true), HORIZONTAL(shouldInvertHorizontal = true),
VERTICAL(shouldInvertVertical = true), VERTICAL(shouldInvertVertical = true),

View File

@@ -13,38 +13,39 @@ import ca.gosyer.jui.domain.reader.model.Direction
import ca.gosyer.jui.domain.reader.model.ImageScale import ca.gosyer.jui.domain.reader.model.ImageScale
import ca.gosyer.jui.domain.reader.model.NavigationMode import ca.gosyer.jui.domain.reader.model.NavigationMode
class ReaderModePreferences(private val mode: String, private val preferenceStore: PreferenceStore) { class ReaderModePreferences(
private val mode: String,
private val preferenceStore: PreferenceStore,
) {
constructor(mode: String, factory: (String) -> PreferenceStore) : constructor(mode: String, factory: (String) -> PreferenceStore) :
this(mode, factory(mode)) this(mode, factory(mode))
private val defaultMode by lazy { DefaultReaderMode.values().find { it.res == mode } } private val defaultMode by lazy { DefaultReaderMode.values().find { it.res == mode } }
fun default(): Preference<Boolean> { fun default(): Preference<Boolean> = preferenceStore.getBoolean("default", defaultMode != null)
return preferenceStore.getBoolean("default", defaultMode != null)
}
fun continuous(): Preference<Boolean> { fun continuous(): Preference<Boolean> = preferenceStore.getBoolean("continuous", defaultMode?.continuous ?: false)
return preferenceStore.getBoolean("continuous", defaultMode?.continuous ?: false)
}
fun direction(): Preference<Direction> { fun direction(): Preference<Direction> =
return preferenceStore.getJsonObject("direction", defaultMode?.direction ?: Direction.Down, Direction.serializer()) preferenceStore.getJsonObject(
} "direction",
defaultMode?.direction ?: Direction.Down,
Direction.serializer(),
)
fun padding(): Preference<Int> { fun padding(): Preference<Int> = preferenceStore.getInt("padding", defaultMode?.padding ?: 0)
return preferenceStore.getInt("padding", defaultMode?.padding ?: 0)
}
fun imageScale(): Preference<ImageScale> { fun imageScale(): Preference<ImageScale> =
return preferenceStore.getJsonObject("image_scale", defaultMode?.imageScale ?: ImageScale.FitScreen, ImageScale.serializer()) preferenceStore.getJsonObject(
} "image_scale",
defaultMode?.imageScale ?: ImageScale.FitScreen,
ImageScale.serializer(),
)
fun fitSize(): Preference<Boolean> { fun fitSize(): Preference<Boolean> = preferenceStore.getBoolean("fit_size", false)
return preferenceStore.getBoolean("fit_size", false)
}
fun maxSize(): Preference<Int> { fun maxSize(): Preference<Int> =
return preferenceStore.getInt( preferenceStore.getInt(
"max_size", "max_size",
if (defaultMode?.continuous == true) { if (defaultMode?.continuous == true) {
if (defaultMode?.direction == Direction.Left || defaultMode?.direction == Direction.Right) { if (defaultMode?.direction == Direction.Left || defaultMode?.direction == Direction.Right) {
@@ -56,9 +57,11 @@ class ReaderModePreferences(private val mode: String, private val preferenceStor
0 0
}, },
) )
}
fun navigationMode(): Preference<NavigationMode> { fun navigationMode(): Preference<NavigationMode> =
return preferenceStore.getJsonObject("navigation", defaultMode?.navigationMode ?: NavigationMode.LNavigation, NavigationMode.serializer()) preferenceStore.getJsonObject(
} "navigation",
defaultMode?.navigationMode ?: NavigationMode.LNavigation,
NavigationMode.serializer(),
)
} }

View File

@@ -12,28 +12,22 @@ import ca.gosyer.jui.domain.reader.model.DefaultReaderMode
import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
class ReaderPreferences(private val preferenceStore: PreferenceStore, private val factory: (String) -> PreferenceStore) { class ReaderPreferences(
fun preload(): Preference<Int> { private val preferenceStore: PreferenceStore,
return preferenceStore.getInt("preload", 3) private val factory: (String) -> PreferenceStore,
} ) {
fun preload(): Preference<Int> = preferenceStore.getInt("preload", 3)
fun threads(): Preference<Int> { fun threads(): Preference<Int> = preferenceStore.getInt("threads", 3)
return preferenceStore.getInt("threads", 3)
}
fun modes(): Preference<List<String>> { fun modes(): Preference<List<String>> =
return preferenceStore.getJsonObject( preferenceStore.getJsonObject(
"modes", "modes",
DefaultReaderMode.values().map { it.res }, DefaultReaderMode.values().map { it.res },
ListSerializer(String.serializer()), ListSerializer(String.serializer()),
) )
}
fun mode(): Preference<String> { fun mode(): Preference<String> = preferenceStore.getString("mode", "RTL")
return preferenceStore.getString("mode", "RTL")
}
fun getMode(mode: String): ReaderModePreferences { fun getMode(mode: String): ReaderModePreferences = ReaderModePreferences(mode, factory)
return ReaderModePreferences(mode, factory)
}
} }

View File

@@ -44,8 +44,8 @@ expect fun HttpClientConfig<HttpClientEngineConfig>.configurePlatform()
fun httpClient( fun httpClient(
serverPreferences: ServerPreferences, serverPreferences: ServerPreferences,
json: Json, json: Json,
): Http { ): Http =
return HttpClient(Engine) { HttpClient(Engine) {
configurePlatform() configurePlatform()
expectSuccess = true expectSuccess = true
@@ -112,10 +112,10 @@ fun httpClient(
} }
logger = object : Logger { logger = object : Logger {
val log = logging("HttpClient") val log = logging("HttpClient")
override fun log(message: String) { override fun log(message: String) {
log.info { message } log.info { message }
} }
} }
} }
} }
}

View File

@@ -25,18 +25,15 @@ class ServerUrlPreference(
private val port: Preference<Int>, private val port: Preference<Int>,
private val pathPrefix: Preference<String>, private val pathPrefix: Preference<String>,
) : Preference<Url> { ) : Preference<Url> {
override fun key(): String { override fun key(): String = key
return key
}
override fun get(): Url { override fun get(): Url =
return URLBuilder(server.get()).apply { URLBuilder(server.get()).apply {
port = this@ServerUrlPreference.port.get() port = this@ServerUrlPreference.port.get()
if (pathPrefix.isSet()) { if (pathPrefix.isSet()) {
pathPrefix.get().ifBlank { null }?.let { path(it) } pathPrefix.get().ifBlank { null }?.let { path(it) }
} }
}.build() }.build()
}
override fun set(value: Url) { override fun set(value: Url) {
server.set(value.protocol.name + "://" + value.host) server.set(value.protocol.name + "://" + value.host)
@@ -44,9 +41,7 @@ class ServerUrlPreference(
pathPrefix.set(value.encodedPath) pathPrefix.set(value.encodedPath)
} }
override fun isSet(): Boolean { override fun isSet(): Boolean = server.isSet() || port.isSet() || pathPrefix.isSet()
return server.isSet() || port.isSet() || pathPrefix.isSet()
}
override fun delete() { override fun delete() {
server.delete() server.delete()
@@ -54,14 +49,13 @@ class ServerUrlPreference(
pathPrefix.delete() pathPrefix.delete()
} }
override fun defaultValue(): Url { override fun defaultValue(): Url =
return URLBuilder(server.defaultValue()).apply { URLBuilder(server.defaultValue()).apply {
port = this@ServerUrlPreference.port.defaultValue() port = this@ServerUrlPreference.port.defaultValue()
}.build() }.build()
}
override fun changes(): Flow<Url> { override fun changes(): Flow<Url> =
return combine(server.getAsFlow(), port.getAsFlow(), pathPrefix.getAsFlow()) { server, port, pathPrefix -> combine(server.getAsFlow(), port.getAsFlow(), pathPrefix.getAsFlow()) { server, port, pathPrefix ->
URLBuilder(server).apply { URLBuilder(server).apply {
this.port = port this.port = port
if (pathPrefix.isNotBlank()) { if (pathPrefix.isNotBlank()) {
@@ -69,9 +63,6 @@ class ServerUrlPreference(
} }
}.build() }.build()
}.drop(1) }.drop(1)
}
override fun stateIn(scope: CoroutineScope): StateFlow<Url> { override fun stateIn(scope: CoroutineScope): StateFlow<Url> = changes().stateIn(scope, SharingStarted.Eagerly, get())
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
} }

View File

@@ -13,51 +13,30 @@ import ca.gosyer.jui.domain.server.model.Proxy
import ca.gosyer.jui.domain.server.model.ServerUrlPreference import ca.gosyer.jui.domain.server.model.ServerUrlPreference
import io.ktor.http.Url import io.ktor.http.Url
class ServerPreferences(private val preferenceStore: PreferenceStore) { class ServerPreferences(
fun server(): Preference<String> { private val preferenceStore: PreferenceStore,
return preferenceStore.getString("server_url", "http://localhost") ) {
} fun server(): Preference<String> = preferenceStore.getString("server_url", "http://localhost")
fun port(): Preference<Int> { fun port(): Preference<Int> = preferenceStore.getInt("server_port", 4567)
return preferenceStore.getInt("server_port", 4567)
}
fun pathPrefix(): Preference<String> { fun pathPrefix(): Preference<String> = preferenceStore.getString("server_path_prefix", "")
return preferenceStore.getString("server_path_prefix", "")
}
fun serverUrl(): Preference<Url> { fun serverUrl(): Preference<Url> = ServerUrlPreference("", server(), port(), pathPrefix())
return ServerUrlPreference("", server(), port(), pathPrefix())
}
fun proxy(): Preference<Proxy> { fun proxy(): Preference<Proxy> = preferenceStore.getJsonObject("proxy", Proxy.NO_PROXY, Proxy.serializer())
return preferenceStore.getJsonObject("proxy", Proxy.NO_PROXY, Proxy.serializer())
}
fun proxyHttpHost(): Preference<String> { fun proxyHttpHost(): Preference<String> = preferenceStore.getString("proxy_http_host")
return preferenceStore.getString("proxy_http_host")
}
fun proxyHttpPort(): Preference<Int> { fun proxyHttpPort(): Preference<Int> = preferenceStore.getInt("proxy_http_port")
return preferenceStore.getInt("proxy_http_port")
}
fun proxySocksHost(): Preference<String> { fun proxySocksHost(): Preference<String> = preferenceStore.getString("proxy_socks_host")
return preferenceStore.getString("proxy_socks_host")
}
fun proxySocksPort(): Preference<Int> { fun proxySocksPort(): Preference<Int> = preferenceStore.getInt("proxy_socks_port")
return preferenceStore.getInt("proxy_socks_port")
}
fun auth(): Preference<Auth> { fun auth(): Preference<Auth> = preferenceStore.getJsonObject("auth", Auth.NONE, Auth.serializer())
return preferenceStore.getJsonObject("auth", Auth.NONE, Auth.serializer())
}
fun authUsername(): Preference<String> { fun authUsername(): Preference<String> = preferenceStore.getString("auth_username")
return preferenceStore.getString("auth_username")
} fun authPassword(): Preference<String> = preferenceStore.getString("auth_password")
fun authPassword(): Preference<String> {
return preferenceStore.getString("auth_password")
}
} }

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class AboutServer class AboutServer
@Inject @Inject
constructor(private val settingsRepository: SettingsRepository) { constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class CheckUpdate class CheckUpdate
@Inject @Inject
constructor(private val settingsRepository: SettingsRepository) { constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetFilterList class GetFilterList
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
reset: Boolean, reset: Boolean,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetLatestManga class GetLatestManga
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
page: Int, page: Int,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetPopularManga class GetPopularManga
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
page: Int, page: Int,

View File

@@ -17,7 +17,9 @@ import org.lighthousegames.logging.logging
class GetQuickSearchManga class GetQuickSearchManga
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
searchTerm: String?, searchTerm: String?,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetSearchManga class GetSearchManga
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
searchTerm: String?, searchTerm: String?,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetSourceList class GetSourceList
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetSourceSettings class GetSourceSettings
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -18,7 +18,9 @@ import org.lighthousegames.logging.logging
class SetSourceFilter class SetSourceFilter
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
filterIndex: Int, filterIndex: Int,
@@ -56,7 +58,8 @@ class SetSourceFilter
.catch { .catch {
onError(it) onError(it)
log.warn(it) { log.warn(it) {
"Failed to set filter for ${source.displayName} with index = $filterIndex and childIndex = $childFilterIndex and value = $filter" "Failed to set filter for ${source.displayName} with index = $filterIndex " +
"and childIndex = $childFilterIndex and value = $filter"
} }
} }
.collect() .collect()
@@ -71,7 +74,10 @@ class SetSourceFilter
) = asFlow(sourceId, filterIndex, childFilterIndex, filter) ) = asFlow(sourceId, filterIndex, childFilterIndex, filter)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to set filter for $sourceId with index = $filterIndex and childIndex = $childFilterIndex and value = $filter" } log.warn(it) {
"Failed to set filter for $sourceId with index = $filterIndex " +
"and childIndex = $childFilterIndex and value = $filter"
}
} }
.collect() .collect()

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class SetSourceSetting class SetSourceSetting
@Inject @Inject
constructor(private val sourceRepository: SourceRepository) { constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await( suspend fun await(
source: Source, source: Source,
settingIndex: Int, settingIndex: Int,

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@Serializable @Serializable
data class SourceFilterChange(val position: Int, val state: String) { data class SourceFilterChange(
val position: Int,
val state: String,
) {
constructor(position: Int, state: Any) : this( constructor(position: Int, state: Any) : this(
position, position,
if (state is SortFilter.Selection) { if (state is SortFilter.Selection) {

View File

@@ -13,7 +13,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
@SerialName("ListPreference") @SerialName("ListPreference")
@Immutable @Immutable
data class ListPreference(override val props: ListProps) : SourcePreference() { data class ListPreference(
override val props: ListProps,
) : SourcePreference() {
@Serializable @Serializable
@Immutable @Immutable
data class ListProps( data class ListProps(

View File

@@ -13,7 +13,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
@SerialName("MultiSelectListPreference") @SerialName("MultiSelectListPreference")
@Immutable @Immutable
data class MultiSelectListPreference(override val props: MultiSelectListProps) : SourcePreference() { data class MultiSelectListPreference(
override val props: MultiSelectListProps,
) : SourcePreference() {
@Serializable @Serializable
@Immutable @Immutable
data class MultiSelectListProps( data class MultiSelectListProps(

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@Serializable @Serializable
data class SourcePreferenceChange(val position: Int, val value: String) { data class SourcePreferenceChange(
val position: Int,
val value: String,
) {
constructor(position: Int, value: Any) : this( constructor(position: Int, value: Any) : this(
position, position,
if (value is List<*>) { if (value is List<*>) {

View File

@@ -11,12 +11,15 @@ import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.library.model.DisplayMode import ca.gosyer.jui.domain.library.model.DisplayMode
class CatalogPreferences(private val preferenceStore: PreferenceStore) { class CatalogPreferences(
fun languages(): Preference<Set<String>> { private val preferenceStore: PreferenceStore,
return preferenceStore.getStringSet("enabled_langs", setOfNotNull("en", Locale.current.language)) ) {
} fun languages(): Preference<Set<String>> = preferenceStore.getStringSet("enabled_langs", setOfNotNull("en", Locale.current.language))
fun displayMode(): Preference<DisplayMode> { fun displayMode(): Preference<DisplayMode> =
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer()) preferenceStore.getJsonObject(
} "display_mode",
DisplayMode.CompactGrid,
DisplayMode.serializer(),
)
} }

View File

@@ -12,68 +12,48 @@ import ca.gosyer.jui.domain.ui.model.StartScreen
import ca.gosyer.jui.domain.ui.model.ThemeMode import ca.gosyer.jui.domain.ui.model.ThemeMode
import ca.gosyer.jui.domain.ui.model.WindowSettings import ca.gosyer.jui.domain.ui.model.WindowSettings
class UiPreferences(private val preferenceStore: PreferenceStore) { class UiPreferences(
fun themeMode(): Preference<ThemeMode> { private val preferenceStore: PreferenceStore,
return preferenceStore.getJsonObject("theme_mode", ThemeMode.System, ThemeMode.serializer()) ) {
} fun themeMode(): Preference<ThemeMode> = preferenceStore.getJsonObject("theme_mode", ThemeMode.System, ThemeMode.serializer())
fun lightTheme(): Preference<Int> { fun lightTheme(): Preference<Int> = preferenceStore.getInt("theme_light", 0)
return preferenceStore.getInt("theme_light", 0)
}
fun darkTheme(): Preference<Int> { fun darkTheme(): Preference<Int> = preferenceStore.getInt("theme_dark", 0)
return preferenceStore.getInt("theme_dark", 0)
}
fun colorPrimaryLight(): Preference<Int> { fun colorPrimaryLight(): Preference<Int> = preferenceStore.getInt("color_primary_light", 0)
return preferenceStore.getInt("color_primary_light", 0)
}
fun colorPrimaryDark(): Preference<Int> { fun colorPrimaryDark(): Preference<Int> = preferenceStore.getInt("color_primary_dark", 0)
return preferenceStore.getInt("color_primary_dark", 0)
}
fun colorSecondaryLight(): Preference<Int> { fun colorSecondaryLight(): Preference<Int> = preferenceStore.getInt("color_secondary_light", 0)
return preferenceStore.getInt("color_secondary_light", 0)
}
fun colorSecondaryDark(): Preference<Int> { fun colorSecondaryDark(): Preference<Int> = preferenceStore.getInt("color_secondary_dark", 0)
return preferenceStore.getInt("color_secondary_dark", 0)
}
fun colorTertiaryLight(): Preference<Int> { fun colorTertiaryLight(): Preference<Int> = preferenceStore.getInt("color_tertiary_light", 0)
return preferenceStore.getInt("color_tertiary_light", 0)
}
fun colorTertiaryDark(): Preference<Int> { fun colorTertiaryDark(): Preference<Int> = preferenceStore.getInt("color_tertiary_dark", 0)
return preferenceStore.getInt("color_tertiary_dark", 0)
}
fun startScreen(): Preference<StartScreen> { fun startScreen(): Preference<StartScreen> =
return preferenceStore.getJsonObject("start_screen", StartScreen.Library, StartScreen.serializer()) preferenceStore.getJsonObject(
} "start_screen",
StartScreen.Library,
StartScreen.serializer(),
)
fun confirmExit(): Preference<Boolean> { fun confirmExit(): Preference<Boolean> = preferenceStore.getBoolean("confirm_exit", false)
return preferenceStore.getBoolean("confirm_exit", false)
}
fun language(): Preference<String> { fun language(): Preference<String> = preferenceStore.getString("language", "")
return preferenceStore.getString("language", "")
}
fun dateFormat(): Preference<String> { fun dateFormat(): Preference<String> = preferenceStore.getString("date_format", "")
return preferenceStore.getString("date_format", "")
}
fun window(): Preference<WindowSettings> { fun window(): Preference<WindowSettings> = preferenceStore.getJsonObject("window", WindowSettings(), WindowSettings.serializer())
return preferenceStore.getJsonObject("window", WindowSettings(), WindowSettings.serializer())
}
fun readerWindow(): Preference<WindowSettings> { fun readerWindow(): Preference<WindowSettings> =
return preferenceStore.getJsonObject("reader_window", WindowSettings(), WindowSettings.serializer()) preferenceStore.getJsonObject(
} "reader_window",
WindowSettings(),
WindowSettings.serializer(),
)
fun windowDecorations(): Preference<Boolean> { fun windowDecorations(): Preference<Boolean> = preferenceStore.getBoolean("window_decorations", true)
return preferenceStore.getBoolean("window_decorations", true)
}
} }

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetRecentUpdates class GetRecentUpdates
@Inject @Inject
constructor(private val updatesRepository: UpdatesRepository) { constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await( suspend fun await(
pageNum: Int, pageNum: Int,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UpdateCategory class UpdateCategory
@Inject @Inject
constructor(private val updatesRepository: UpdatesRepository) { constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await( suspend fun await(
categoryId: Long, categoryId: Long,
onError: suspend (Throwable) -> Unit = {}, onError: suspend (Throwable) -> Unit = {},

View File

@@ -52,7 +52,10 @@ class UpdateChecker
}.flowOn(Dispatchers.IO) }.flowOn(Dispatchers.IO)
sealed class Update { sealed class Update {
data class UpdateFound(val release: GithubRelease) : Update() data class UpdateFound(
val release: GithubRelease,
) : Update()
object NoUpdatesFound : Update() object NoUpdatesFound : Update()
} }

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class UpdateLibrary class UpdateLibrary
@Inject @Inject
constructor(private val updatesRepository: UpdatesRepository) { constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow() asFlow()
.catch { .catch {

View File

@@ -157,10 +157,15 @@ class UpdatesPager
@Immutable @Immutable
sealed class Updates { sealed class Updates {
@Immutable @Immutable
data class Update(val manga: Manga, val chapter: Chapter) : Updates() data class Update(
val manga: Manga,
val chapter: Chapter,
) : Updates()
@Immutable @Immutable
data class Date(val date: String) : Updates() { data class Date(
val date: String,
) : Updates() {
constructor(date: LocalDate) : this(date.toString()) constructor(date: LocalDate) : this(date.toString())
} }
} }

View File

@@ -9,8 +9,8 @@ package ca.gosyer.jui.domain.updates.service
import ca.gosyer.jui.core.prefs.Preference import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore import ca.gosyer.jui.core.prefs.PreferenceStore
class UpdatePreferences(private val preferenceStore: PreferenceStore) { class UpdatePreferences(
fun enabled(): Preference<Boolean> { private val preferenceStore: PreferenceStore,
return preferenceStore.getBoolean("enabled", true) ) {
} fun enabled(): Preference<Boolean> = preferenceStore.getBoolean("enabled", true)
} }

View File

@@ -10,80 +10,74 @@ import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.server.service.host.ServerHostPreference import ca.gosyer.jui.domain.server.service.host.ServerHostPreference
class ServerHostPreferences(private val preferenceStore: PreferenceStore) { class ServerHostPreferences(
fun host(): Preference<Boolean> { private val preferenceStore: PreferenceStore,
return preferenceStore.getBoolean("host", true) ) {
} fun host(): Preference<Boolean> = preferenceStore.getBoolean("host", true)
private val ip = ServerHostPreference.IP(preferenceStore) private val ip = ServerHostPreference.IP(preferenceStore)
fun ip(): Preference<String> {
return ip.preference() fun ip(): Preference<String> = ip.preference()
}
private val port = ServerHostPreference.Port(preferenceStore) private val port = ServerHostPreference.Port(preferenceStore)
fun port(): Preference<Int> {
return port.preference() fun port(): Preference<Int> = port.preference()
}
// Proxy // Proxy
private val socksProxyEnabled = ServerHostPreference.SocksProxyEnabled(preferenceStore) private val socksProxyEnabled = ServerHostPreference.SocksProxyEnabled(preferenceStore)
fun socksProxyEnabled(): Preference<Boolean> {
return socksProxyEnabled.preference() fun socksProxyEnabled(): Preference<Boolean> = socksProxyEnabled.preference()
}
private val socksProxyHost = ServerHostPreference.SocksProxyHost(preferenceStore) private val socksProxyHost = ServerHostPreference.SocksProxyHost(preferenceStore)
fun socksProxyHost(): Preference<String> {
return socksProxyHost.preference() fun socksProxyHost(): Preference<String> = socksProxyHost.preference()
}
private val socksProxyPort = ServerHostPreference.SocksProxyPort(preferenceStore) private val socksProxyPort = ServerHostPreference.SocksProxyPort(preferenceStore)
fun socksProxyPort(): Preference<Int> {
return socksProxyPort.preference() fun socksProxyPort(): Preference<Int> = socksProxyPort.preference()
}
// Misc // Misc
private val debugLogsEnabled = ServerHostPreference.DebugLogsEnabled(preferenceStore) private val debugLogsEnabled = ServerHostPreference.DebugLogsEnabled(preferenceStore)
fun debugLogsEnabled(): Preference<Boolean> {
return debugLogsEnabled.preference() fun debugLogsEnabled(): Preference<Boolean> = debugLogsEnabled.preference()
}
private val systemTrayEnabled = ServerHostPreference.SystemTrayEnabled(preferenceStore) private val systemTrayEnabled = ServerHostPreference.SystemTrayEnabled(preferenceStore)
fun systemTrayEnabled(): Preference<Boolean> {
return systemTrayEnabled.preference() fun systemTrayEnabled(): Preference<Boolean> = systemTrayEnabled.preference()
}
// Downloader // Downloader
private val downloadPath = ServerHostPreference.DownloadPath(preferenceStore) private val downloadPath = ServerHostPreference.DownloadPath(preferenceStore)
fun downloadPath(): Preference<String> {
return downloadPath.preference() fun downloadPath(): Preference<String> = downloadPath.preference()
}
private val downloadAsCbz = ServerHostPreference.DownloadAsCbz(preferenceStore) private val downloadAsCbz = ServerHostPreference.DownloadAsCbz(preferenceStore)
fun downloadAsCbz(): Preference<Boolean> {
return downloadAsCbz.preference() fun downloadAsCbz(): Preference<Boolean> = downloadAsCbz.preference()
}
// WebUI // WebUI
private val webUIEnabled = ServerHostPreference.WebUIEnabled(preferenceStore) private val webUIEnabled = ServerHostPreference.WebUIEnabled(preferenceStore)
fun webUIEnabled(): Preference<Boolean> {
return webUIEnabled.preference() fun webUIEnabled(): Preference<Boolean> = webUIEnabled.preference()
}
private val openInBrowserEnabled = ServerHostPreference.OpenInBrowserEnabled(preferenceStore) private val openInBrowserEnabled = ServerHostPreference.OpenInBrowserEnabled(preferenceStore)
fun openInBrowserEnabled(): Preference<Boolean> {
return openInBrowserEnabled.preference() fun openInBrowserEnabled(): Preference<Boolean> = openInBrowserEnabled.preference()
}
// Authentication // Authentication
private val basicAuthEnabled = ServerHostPreference.BasicAuthEnabled(preferenceStore) private val basicAuthEnabled = ServerHostPreference.BasicAuthEnabled(preferenceStore)
fun basicAuthEnabled(): Preference<Boolean> {
return basicAuthEnabled.preference()
}
private val basicAuthUsername = ServerHostPreference.BasicAuthUsername(preferenceStore)
fun basicAuthUsername(): Preference<String> {
return basicAuthUsername.preference()
}
private val basicAuthPassword = ServerHostPreference.BasicAuthPassword(preferenceStore)
fun basicAuthPassword(): Preference<String> {
return basicAuthPassword.preference()
}
fun properties(): Array<String> { fun basicAuthEnabled(): Preference<Boolean> = basicAuthEnabled.preference()
return listOf(
private val basicAuthUsername = ServerHostPreference.BasicAuthUsername(preferenceStore)
fun basicAuthUsername(): Preference<String> = basicAuthUsername.preference()
private val basicAuthPassword = ServerHostPreference.BasicAuthPassword(preferenceStore)
fun basicAuthPassword(): Preference<String> = basicAuthPassword.preference()
fun properties(): Array<String> =
listOf(
ip, ip,
port, port,
socksProxyEnabled, socksProxyEnabled,
@@ -101,5 +95,4 @@ class ServerHostPreferences(private val preferenceStore: PreferenceStore) {
).mapNotNull { ).mapNotNull {
it.getProperty() it.getProperty()
}.toTypedArray() }.toTypedArray()
}
} }

View File

@@ -16,6 +16,7 @@ sealed class ServerHostPreference<T : Any> {
protected abstract val defaultValue: T protected abstract val defaultValue: T
protected abstract val serverValue: T protected abstract val serverValue: T
private fun validate(value: T): Boolean { private fun validate(value: T): Boolean {
return value != serverValue return value != serverValue
} }
@@ -30,6 +31,7 @@ sealed class ServerHostPreference<T : Any> {
} }
protected abstract val preferenceStore: PreferenceStore protected abstract val preferenceStore: PreferenceStore
abstract fun preference(): Preference<T> abstract fun preference(): Preference<T>
companion object { companion object {
@@ -42,114 +44,144 @@ sealed class ServerHostPreference<T : Any> {
override val defaultValue: String, override val defaultValue: String,
override val serverValue: String = defaultValue, override val serverValue: String = defaultValue,
) : ServerHostPreference<String>() { ) : ServerHostPreference<String>() {
override fun preference(): Preference<String> { override fun preference(): Preference<String> = preferenceStore.getString(propertyName, defaultValue)
return preferenceStore.getString(propertyName, defaultValue)
}
} }
sealed class IntServerHostPreference( sealed class IntServerHostPreference(
override val preferenceStore: PreferenceStore, override val preferenceStore: PreferenceStore,
override val propertyName: String, override val propertyName: String,
override val defaultValue: Int, override val defaultValue: Int,
override val serverValue: Int = defaultValue, override val serverValue: Int = defaultValue,
) : ServerHostPreference<Int>() { ) : ServerHostPreference<Int>() {
override fun preference(): Preference<Int> { override fun preference(): Preference<Int> = preferenceStore.getInt(propertyName, defaultValue)
return preferenceStore.getInt(propertyName, defaultValue)
}
} }
sealed class BooleanServerHostPreference( sealed class BooleanServerHostPreference(
override val preferenceStore: PreferenceStore, override val preferenceStore: PreferenceStore,
override val propertyName: String, override val propertyName: String,
override val defaultValue: Boolean, override val defaultValue: Boolean,
override val serverValue: Boolean = defaultValue, override val serverValue: Boolean = defaultValue,
) : ServerHostPreference<Boolean>() { ) : ServerHostPreference<Boolean>() {
override fun preference(): Preference<Boolean> { override fun preference(): Preference<Boolean> = preferenceStore.getBoolean(propertyName, defaultValue)
return preferenceStore.getBoolean(propertyName, defaultValue)
}
} }
class IP(preferenceStore: PreferenceStore) : StringServerHostPreference( class IP(
preferenceStore, preferenceStore: PreferenceStore,
"ip", ) : StringServerHostPreference(
"0.0.0.0", preferenceStore,
) "ip",
class Port(override val preferenceStore: PreferenceStore) : IntServerHostPreference( "0.0.0.0",
preferenceStore, )
"port",
4567, class Port(
) override val preferenceStore: PreferenceStore,
) : IntServerHostPreference(
preferenceStore,
"port",
4567,
)
// Proxy // Proxy
class SocksProxyEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class SocksProxyEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"socksProxyEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
) "socksProxyEnabled",
class SocksProxyHost(preferenceStore: PreferenceStore) : StringServerHostPreference( false,
preferenceStore, )
"socksProxyHost",
"", class SocksProxyHost(
) preferenceStore: PreferenceStore,
class SocksProxyPort(override val preferenceStore: PreferenceStore) : IntServerHostPreference( ) : StringServerHostPreference(
preferenceStore, preferenceStore,
"socksProxyPort", "socksProxyHost",
0, "",
) )
class SocksProxyPort(
override val preferenceStore: PreferenceStore,
) : IntServerHostPreference(
preferenceStore,
"socksProxyPort",
0,
)
// Misc // Misc
class DebugLogsEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class DebugLogsEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"debugLogsEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
) "debugLogsEnabled",
false,
)
class SystemTrayEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class SystemTrayEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"systemTrayEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
true, "systemTrayEnabled",
) false,
true,
)
// Downloader // Downloader
class DownloadPath(preferenceStore: PreferenceStore) : StringServerHostPreference( class DownloadPath(
preferenceStore, preferenceStore: PreferenceStore,
"downloadsPath", ) : StringServerHostPreference(
"", preferenceStore,
) "downloadsPath",
class DownloadAsCbz(preferenceStore: PreferenceStore) : BooleanServerHostPreference( "",
preferenceStore, )
"downloadAsCbz",
false, class DownloadAsCbz(
) preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"downloadAsCbz",
false,
)
// WebUI // WebUI
class WebUIEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class WebUIEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"webUIEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
true, "webUIEnabled",
) false,
true,
)
class OpenInBrowserEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class OpenInBrowserEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"initialOpenInBrowserEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
true, "initialOpenInBrowserEnabled",
) false,
true,
)
// Authentication // Authentication
class BasicAuthEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference( class BasicAuthEnabled(
preferenceStore, preferenceStore: PreferenceStore,
"basicAuthEnabled", ) : BooleanServerHostPreference(
false, preferenceStore,
) "basicAuthEnabled",
class BasicAuthUsername(preferenceStore: PreferenceStore) : StringServerHostPreference( false,
preferenceStore, )
"basicAuthUsername",
"", class BasicAuthUsername(
) preferenceStore: PreferenceStore,
class BasicAuthPassword(preferenceStore: PreferenceStore) : StringServerHostPreference( ) : StringServerHostPreference(
preferenceStore, preferenceStore,
"basicAuthPassword", "basicAuthUsername",
"", "",
) )
class BasicAuthPassword(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"basicAuthPassword",
"",
)
} }

View File

@@ -20,7 +20,10 @@ import me.tatarka.inject.annotations.Provides
abstract class AppComponent( abstract class AppComponent(
@get:Provides @get:Provides
val context: ContextWrapper, val context: ContextWrapper,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent { ) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations abstract val appMigrations: AppMigrations
@get:AppScope @get:AppScope

View File

@@ -4,11 +4,9 @@ package ca.gosyer.jui.ios
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.Text import androidx.compose.material.Text
@@ -33,34 +31,10 @@ import ca.gosyer.jui.uicore.vm.ContextWrapper
import ca.gosyer.jui.uicore.vm.Length import ca.gosyer.jui.uicore.vm.Length
import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.autoreleasepool
import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues
import kotlinx.cinterop.useContents
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.lighthousegames.logging.FixedLogLevel
import org.lighthousegames.logging.KmLog
import org.lighthousegames.logging.KmLogging
import org.lighthousegames.logging.LogFactory
import org.lighthousegames.logging.LogLevel
import org.lighthousegames.logging.LogLevelController
import org.lighthousegames.logging.Logger
import org.lighthousegames.logging.TagProvider
import platform.Foundation.NSStringFromClass
import platform.Foundation.NSThread
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.UIViewController import platform.UIKit.UIViewController
import platform.UIKit.UIWindow
import platform.UIKit.safeAreaInsets
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds

View File

@@ -17,7 +17,9 @@ import androidx.compose.ui.platform.LocalContext
import okio.Source import okio.Source
import okio.source import okio.source
actual class FileChooser(private val resultLauncher: ManagedActivityResultLauncher<String, Uri?>) { actual class FileChooser(
private val resultLauncher: ManagedActivityResultLauncher<String, Uri?>,
) {
actual fun launch(extension: String) { actual fun launch(extension: String) {
resultLauncher.launch(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)) resultLauncher.launch(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension))
} }

View File

@@ -10,5 +10,6 @@ import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.component.decoder.BitmapFactoryDecoder import com.seiko.imageloader.component.decoder.BitmapFactoryDecoder
import com.seiko.imageloader.component.decoder.Decoder import com.seiko.imageloader.component.decoder.Decoder
actual class BitmapDecoderFactory actual constructor(contextWrapper: ContextWrapper) : actual class BitmapDecoderFactory actual constructor(
Decoder.Factory by BitmapFactoryDecoder.Factory(contextWrapper, Int.MAX_VALUE) contextWrapper: ContextWrapper,
) : Decoder.Factory by BitmapFactoryDecoder.Factory(contextWrapper, Int.MAX_VALUE)

View File

@@ -15,11 +15,10 @@ internal actual fun Color.toHsv(): FloatArray {
return result return result
} }
internal actual fun hexStringToColor(hex: String): Color? { internal actual fun hexStringToColor(hex: String): Color? =
return try { try {
val color = android.graphics.Color.parseColor(hex) val color = android.graphics.Color.parseColor(hex)
Color(color) Color(color)
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
}

View File

@@ -107,22 +107,20 @@ internal class SavedStateHandlesProvider(
/** /**
* Restore the state associated with a particular SavedStateHandle, identified by its [key] * Restore the state associated with a particular SavedStateHandle, identified by its [key]
*/ */
fun consumeRestoredStateForKey(key: String): Bundle? { fun consumeRestoredStateForKey(key: String): Bundle? =
return savedStateRegistry::class.java savedStateRegistry::class.java
.methods .methods
.find { it.name == "consumeRestoredStateForKey" } .find { it.name == "consumeRestoredStateForKey" }
?.invoke(savedStateRegistry, key) as? Bundle ?.invoke(savedStateRegistry, key) as? Bundle
}
} }
inline fun <reified T : ScreenModel> CreationExtras.addScreenModelKey( inline fun <reified T : ScreenModel> CreationExtras.addScreenModelKey(
screen: Screen, screen: Screen,
tag: String?, tag: String?,
): CreationExtras { ): CreationExtras =
return MutableCreationExtras(this).apply { MutableCreationExtras(this).apply {
set( set(
VIEW_MODEL_KEY, VIEW_MODEL_KEY,
"${screen.key}:${T::class.qualifiedName}:${tag ?: "default"}", "${screen.key}:${T::class.qualifiedName}:${tag ?: "default"}",
) )
} }
}

View File

@@ -15,7 +15,5 @@ actual object ThemeScrollbarStyle {
@Stable @Stable
@Composable @Composable
actual fun getScrollbarStyle(): ScrollbarStyle { actual fun getScrollbarStyle(): ScrollbarStyle = defaultScrollbarStyle
return defaultScrollbarStyle
}
} }

View File

@@ -11,7 +11,9 @@ import androidx.compose.runtime.remember
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
actual class CategoriesLauncher(private val navigator: Navigator?) { actual class CategoriesLauncher(
private val navigator: Navigator?,
) {
actual fun open() { actual fun open() {
navigator?.push(CategoriesScreen()) navigator?.push(CategoriesScreen())
} }

View File

@@ -9,8 +9,8 @@ package ca.gosyer.jui.ui.main.about.components
import android.os.Build import android.os.Build
import ca.gosyer.jui.presentation.build.BuildKonfig import ca.gosyer.jui.presentation.build.BuildKonfig
actual fun getDebugInfo(): String { actual fun getDebugInfo(): String =
return """ """
App version: ${BuildKonfig.VERSION} (${ if (BuildKonfig.DEBUG) "Debug" else "Standard"}, ${BuildKonfig.MIGRATION_CODE}) App version: ${BuildKonfig.VERSION} (${ if (BuildKonfig.DEBUG) "Debug" else "Standard"}, ${BuildKonfig.MIGRATION_CODE})
Preview build: r${BuildKonfig.PREVIEW_BUILD} Preview build: r${BuildKonfig.PREVIEW_BUILD}
Android version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT}) Android version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})
@@ -20,5 +20,4 @@ actual fun getDebugInfo(): String {
Device name: ${Build.DEVICE} Device name: ${Build.DEVICE}
Device model: ${Build.MODEL} Device model: ${Build.MODEL}
Device product name: ${Build.PRODUCT} Device product name: ${Build.PRODUCT}
""".trimIndent() """.trimIndent()
}

View File

@@ -12,7 +12,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
actual class ReaderLauncher(private val context: Context) { actual class ReaderLauncher(
private val context: Context,
) {
actual fun launch( actual fun launch(
chapterIndex: Int, chapterIndex: Int,
mangaId: Long, mangaId: Long,

View File

@@ -13,10 +13,10 @@ import ca.gosyer.jui.uicore.vm.ViewModel
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
@Composable @Composable
actual fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit { actual fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit = {}
return {}
}
actual class SettingsServerHostViewModel actual class SettingsServerHostViewModel
@Inject @Inject
constructor(contextWrapper: ContextWrapper) : ViewModel(contextWrapper) constructor(
contextWrapper: ContextWrapper,
) : ViewModel(contextWrapper)

View File

@@ -13,6 +13,4 @@ import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsChannel import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.javaio.toInputStream import io.ktor.utils.io.jvm.javaio.toInputStream
actual suspend fun HttpResponse.toImageBitmap(): ImageBitmap { actual suspend fun HttpResponse.toImageBitmap(): ImageBitmap = BitmapFactory.decodeStream(bodyAsChannel().toInputStream()).asImageBitmap()
return BitmapFactory.decodeStream(bodyAsChannel().toInputStream()).asImageBitmap()
}

View File

@@ -91,9 +91,7 @@ data class ChapterDownloadItem(
_downloadState.value = ChapterDownloadState.NotDownloaded _downloadState.value = ChapterDownloadState.NotDownloaded
} }
fun isSelected(selectedItems: List<Long>): Boolean { fun isSelected(selectedItems: List<Long>): Boolean = (chapter.id in selectedItems).also { _isSelected.value = it }
return (chapter.id in selectedItems).also { _isSelected.value = it }
}
} }
enum class ChapterDownloadState { enum class ChapterDownloadState {

View File

@@ -29,8 +29,8 @@ fun getMaterialDialogProperties(
title: String = BuildKonfig.NAME, title: String = BuildKonfig.NAME,
icon: Painter = MR.images.icon.toPainter(), icon: Painter = MR.images.icon.toPainter(),
resizable: Boolean = true, resizable: Boolean = true,
): MaterialDialogProperties { ): MaterialDialogProperties =
return MaterialDialogProperties( MaterialDialogProperties(
dismissOnBackPress = dismissOnBackPress, dismissOnBackPress = dismissOnBackPress,
dismissOnClickOutside = dismissOnClickOutside, dismissOnClickOutside = dismissOnClickOutside,
securePolicy = securePolicy, securePolicy = securePolicy,
@@ -41,4 +41,3 @@ fun getMaterialDialogProperties(
windowIcon = icon, windowIcon = icon,
windowIsResizable = resizable, windowIsResizable = resizable,
) )
}

View File

@@ -9,4 +9,6 @@ package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.component.decoder.Decoder import com.seiko.imageloader.component.decoder.Decoder
expect class BitmapDecoderFactory constructor(contextWrapper: ContextWrapper) : Decoder.Factory expect class BitmapDecoderFactory constructor(
contextWrapper: ContextWrapper,
) : Decoder.Factory

View File

@@ -10,11 +10,15 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
@Stable @Stable
class StableHolder<T>(val item: T) { class StableHolder<T>(
val item: T,
) {
operator fun component1(): T = item operator fun component1(): T = item
} }
@Immutable @Immutable
class ImmutableHolder<T>(val item: T) { class ImmutableHolder<T>(
val item: T,
) {
operator fun component1(): T = item operator fun component1(): T = item
} }

View File

@@ -75,7 +75,8 @@ enum class OverflowMode {
@Composable @Composable
fun ActionMenu( fun ActionMenu(
items: ImmutableList<Action>, items: ImmutableList<Action>,
numIcons: Int = 3, // includes overflow menu icon; may be overridden by NEVER_OVERFLOW // includes overflow menu icon; may be overridden by NEVER_OVERFLOW
numIcons: Int = 3,
menuVisible: MutableState<Boolean> = remember { mutableStateOf(false) }, menuVisible: MutableState<Boolean> = remember { mutableStateOf(false) },
iconItem: @Composable (onClick: () -> Unit, name: String, icon: ImageVector, enabled: Boolean) -> Unit, iconItem: @Composable (onClick: () -> Unit, name: String, icon: ImageVector, enabled: Boolean) -> Unit,
) { ) {
@@ -99,7 +100,9 @@ fun ActionMenu(
} else { } else {
TextButton( TextButton(
onClick = when (item) { onClick = when (item) {
is ActionGroup -> { { openGroup = item } } is ActionGroup -> {
{ openGroup = item }
}
is ActionItem -> item.doAction is ActionItem -> item.doAction
}, },
enabled = item.enabled, enabled = item.enabled,

View File

@@ -24,6 +24,7 @@ class DisplayController(
fun openSideMenu() { fun openSideMenu() {
_sideMenuVisible.value = true _sideMenuVisible.value = true
} }
fun closeSideMenu() { fun closeSideMenu() {
_sideMenuVisible.value = false _sideMenuVisible.value = false
} }

View File

@@ -88,8 +88,10 @@ fun Toolbar(
closeIcon: ImageVector = ToolbarDefault, closeIcon: ImageVector = ToolbarDefault,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
actions: @Composable () -> ImmutableList<Action> = { remember { persistentListOf() } }, actions: @Composable () -> ImmutableList<Action> = { remember { persistentListOf() } },
backgroundColor: Color = MaterialTheme.colors.surface, // CustomColors.current.bars, // CustomColors.current.bars,
contentColor: Color = contentColorFor(backgroundColor), // CustomColors.current.onBars, backgroundColor: Color = MaterialTheme.colors.surface,
// CustomColors.current.onBars,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = Dp.Hairline, elevation: Dp = Dp.Hairline,
searchText: String? = null, searchText: String? = null,
search: ((String) -> Unit)? = null, search: ((String) -> Unit)? = null,
@@ -216,6 +218,7 @@ private fun ThinToolbar(
searchSubmit: (() -> Unit)?, searchSubmit: (() -> Unit)?,
) { ) {
var searchMode by remember { mutableStateOf(!searchText.isNullOrEmpty()) } var searchMode by remember { mutableStateOf(!searchText.isNullOrEmpty()) }
fun closeSearch() { fun closeSearch() {
search?.invoke("") search?.invoke("")
searchSubmit?.invoke() searchSubmit?.invoke()

View File

@@ -421,16 +421,12 @@ private fun satValToCoordinates(
saturation: Float, saturation: Float,
value: Float, value: Float,
size: IntSize, size: IntSize,
): Offset { ): Offset = Offset(saturation * size.width, ((1f - value) * size.height))
return Offset(saturation * size.width, ((1f - value) * size.height))
}
private fun hueToCoordinate( private fun hueToCoordinate(
hue: Float, hue: Float,
size: IntSize, size: IntSize,
): Float { ): Float = size.height - (hue * size.height / 360f)
return size.height - (hue * size.height / 360f)
}
// Color space conversions // Color space conversions
@@ -439,36 +435,51 @@ fun hsvToColor(
hue: Float, hue: Float,
saturation: Float, saturation: Float,
value: Float, value: Float,
): Color { ): Color = Color.hsv(hue, saturation.coerceIn(0F, 1F), value.coerceIn(0F, 1F))
return Color.hsv(hue, saturation.coerceIn(0F, 1F), value.coerceIn(0F, 1F))
}
internal expect fun Color.toHsv(): FloatArray internal expect fun Color.toHsv(): FloatArray
private fun hueToColor(hue: Float): Color { private fun hueToColor(hue: Float): Color = hsvToColor(hue, 1f, 1f)
return hsvToColor(hue, 1f, 1f)
}
internal expect fun hexStringToColor(hex: String): Color? internal expect fun hexStringToColor(hex: String): Color?
private val presetColors = listOf( private val presetColors = listOf(
Color(0xFFF44336), // RED 500 // RED 500
Color(0xFFE91E63), // PINK 500 Color(0xFFF44336),
Color(0xFFFF2C93), // LIGHT PINK 500 // PINK 500
Color(0xFF9C27B0), // PURPLE 500 Color(0xFFE91E63),
Color(0xFF673AB7), // DEEP PURPLE 500 // LIGHT PINK 500
Color(0xFF3F51B5), // INDIGO 500 Color(0xFFFF2C93),
Color(0xFF2196F3), // BLUE 500 // PURPLE 500
Color(0xFF03A9F4), // LIGHT BLUE 500 Color(0xFF9C27B0),
Color(0xFF00BCD4), // CYAN 500 // DEEP PURPLE 500
Color(0xFF009688), // TEAL 500 Color(0xFF673AB7),
Color(0xFF4CAF50), // GREEN 500 // INDIGO 500
Color(0xFF8BC34A), // LIGHT GREEN 500 Color(0xFF3F51B5),
Color(0xFFCDDC39), // LIME 500 // BLUE 500
Color(0xFFFFEB3B), // YELLOW 500 Color(0xFF2196F3),
Color(0xFFFFC107), // AMBER 500 // LIGHT BLUE 500
Color(0xFFFF9800), // ORANGE 500 Color(0xFF03A9F4),
Color(0xFF795548), // BROWN 500 // CYAN 500
Color(0xFF607D8B), // BLUE GREY 500 Color(0xFF00BCD4),
Color(0xFF9E9E9E), // GREY 500 // TEAL 500
Color(0xFF009688),
// GREEN 500
Color(0xFF4CAF50),
// LIGHT GREEN 500
Color(0xFF8BC34A),
// LIME 500
Color(0xFFCDDC39),
// YELLOW 500
Color(0xFFFFEB3B),
// AMBER 500
Color(0xFFFFC107),
// ORANGE 500
Color(0xFFFF9800),
// BROWN 500
Color(0xFF795548),
// BLUE GREY 500
Color(0xFF607D8B),
// GREY 500
Color(0xFF9E9E9E),
).toImmutableList() ).toImmutableList()

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