Automatic Lint

This commit is contained in:
Syer10
2023-07-04 02:22:36 +00:00
parent c4fc80510c
commit ed7c99a1ab
278 changed files with 6866 additions and 5684 deletions

View File

@@ -21,7 +21,6 @@ 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

@@ -23,7 +23,6 @@ abstract class AppComponent(
@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
@@ -39,7 +38,8 @@ abstract class AppComponent(
private var appComponentInstance: AppComponent? = null private var appComponentInstance: AppComponent? = null
@Suppress("UNRESOLVED_REFERENCE", "EXPRESSION_EXPECTED_PACKAGE_FOUND") @Suppress("UNRESOLVED_REFERENCE", "EXPRESSION_EXPECTED_PACKAGE_FOUND")
fun getInstance(context: Context) = appComponentInstance ?: create(context) fun getInstance(context: Context) =
appComponentInstance ?: create(context)
.also { appComponentInstance = it } .also { appComponentInstance = it }
} }
} }

View File

@@ -11,11 +11,12 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.ContextWrapper
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
class AppMigrations @Inject constructor( class AppMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences, private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper, private val contextWrapper: ContextWrapper,
) { ) {
fun runMigrations(): Boolean { fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get() val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.VERSION_CODE) { if (oldVersion < BuildConfig.VERSION_CODE) {
@@ -31,4 +32,4 @@ class AppMigrations @Inject constructor(
} }
return false return false
} }
} }

View File

@@ -16,9 +16,12 @@ import ca.gosyer.jui.ui.base.theme.AppTheme
import ca.gosyer.jui.ui.reader.ReaderMenu import ca.gosyer.jui.ui.reader.ReaderMenu
class ReaderActivity : AppCompatActivity() { class ReaderActivity : AppCompatActivity() {
companion object { companion object {
fun newIntent(context: Context, mangaId: Long, chapterIndex: Int): Intent { fun newIntent(
context: Context,
mangaId: Long,
chapterIndex: Int,
): Intent {
return Intent(context, ReaderActivity::class.java).apply { return Intent(context, ReaderActivity::class.java).apply {
putExtra("manga", mangaId) putExtra("manga", mangaId)
putExtra("chapter", chapterIndex) putExtra("chapter", chapterIndex)

View File

@@ -55,11 +55,13 @@ import org.lighthousegames.logging.logging
import java.util.regex.Pattern import java.util.regex.Pattern
class AndroidDownloadService : Service() { class AndroidDownloadService : Service() {
companion object { companion object {
private var instance: AndroidDownloadService? = null private var instance: AndroidDownloadService? = null
fun start(context: Context, actions: Actions) { fun start(
context: Context,
actions: Actions,
) {
if (!isRunning() && actions != Actions.STOP) { if (!isRunning() && actions != Actions.STOP) {
val intent = Intent(context, AndroidDownloadService::class.java).apply { val intent = Intent(context, AndroidDownloadService::class.java).apply {
action = actions.name action = actions.name
@@ -106,7 +108,11 @@ class AndroidDownloadService : Service() {
super.onDestroy() super.onDestroy()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int,
): Int {
instance = this instance = this
ioScope.coroutineContext.job.cancelChildren() ioScope.coroutineContext.job.cancelChildren()

View File

@@ -55,11 +55,13 @@ import kotlinx.serialization.json.Json
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class AndroidLibraryService : Service() { class AndroidLibraryService : Service() {
companion object { companion object {
private var instance: AndroidLibraryService? = null private var instance: AndroidLibraryService? = null
fun start(context: Context, actions: Actions) { fun start(
context: Context,
actions: Actions,
) {
if (!isRunning() && actions != Actions.STOP) { if (!isRunning() && actions != Actions.STOP) {
val intent = Intent(context, AndroidLibraryService::class.java).apply { val intent = Intent(context, AndroidLibraryService::class.java).apply {
action = actions.name action = actions.name
@@ -106,7 +108,11 @@ class AndroidLibraryService : Service() {
super.onDestroy() super.onDestroy()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int,
): Int {
instance = this instance = this
ioScope.coroutineContext.job.cancelChildren() ioScope.coroutineContext.job.cancelChildren()

View File

@@ -14,7 +14,6 @@ import ca.gosyer.jui.i18n.MR
import dev.icerock.moko.resources.desc.desc import dev.icerock.moko.resources.desc.desc
object Notifications { object Notifications {
/** /**
* Notification channel and ids used by the downloader. * Notification channel and ids used by the downloader.
*/ */

View File

@@ -37,7 +37,10 @@ fun Context.acquireWakeLock(tag: String): PowerManager.WakeLock {
* @param block the function that will execute inside the builder. * @param block the function that will execute inside the builder.
* @return a notification to be displayed or updated. * @return a notification to be displayed or updated.
*/ */
fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder { fun Context.notificationBuilder(
channelId: String,
block: (NotificationCompat.Builder.() -> Unit)? = null,
): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(this, channelId) val builder = NotificationCompat.Builder(this, channelId)
// .setColor(getColor(R.color.accent_blue)) // .setColor(getColor(R.color.accent_blue))
if (block != null) { if (block != null) {
@@ -53,7 +56,10 @@ fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Bu
* @param block the function that will execute inside the builder. * @param block the function that will execute inside the builder.
* @return a notification to be displayed or updated. * @return a notification to be displayed or updated.
*/ */
fun Context.notification(channelId: String, block: (NotificationCompat.Builder.() -> Unit)?): Notification { fun Context.notification(
channelId: String,
block: (NotificationCompat.Builder.() -> Unit)?,
): Notification {
val builder = notificationBuilder(channelId, block) val builder = notificationBuilder(channelId, block)
return builder.build() return builder.build()
} }

View File

@@ -10,7 +10,9 @@ import android.content.Context
import com.russhwolf.settings.SharedPreferencesSettings import com.russhwolf.settings.SharedPreferencesSettings
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
actual class PreferenceStoreFactory @Inject constructor(private val context: Context) { actual class PreferenceStoreFactory
@Inject
constructor(private val context: Context) {
actual fun create(vararg names: String): PreferenceStore { actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore( return StandardPreferenceStore(
SharedPreferencesSettings( SharedPreferencesSettings(
@@ -21,4 +23,4 @@ actual class PreferenceStoreFactory @Inject constructor(private val context: Con
), ),
) )
} }
} }

View File

@@ -48,17 +48,11 @@ fun CoroutineScope.launchIO(
block: suspend CoroutineScope.() -> Unit, block: suspend CoroutineScope.() -> Unit,
) = launch(Dispatchers.IO, start, block) ) = launch(Dispatchers.IO, start, block)
suspend fun <T> withDefaultContext( suspend fun <T> withDefaultContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Default, block)
block: suspend CoroutineScope.() -> T,
): T = withContext(Dispatchers.Default, block)
suspend fun <T> withUIContext( suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Main, block)
block: suspend CoroutineScope.() -> T,
): T = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext( suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)
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 }

View File

@@ -29,7 +29,6 @@ internal open class ProcessChannel<T>(
internal val inChannel: Channel<T>, internal val inChannel: Channel<T>,
internal val outChannel: Channel<T>, internal val outChannel: Channel<T>,
) : Channel<T> { ) : Channel<T> {
@DelicateCoroutinesApi @DelicateCoroutinesApi
override val isClosedForReceive: Boolean override val isClosedForReceive: Boolean
get() = outChannel.isClosedForReceive get() = outChannel.isClosedForReceive

View File

@@ -10,7 +10,10 @@ package ca.gosyer.jui.core.lang
* Replaces the given string to have at most [count] characters using [replacement] at its end. * Replaces the given string to have at most [count] characters using [replacement] at its end.
* If [replacement] is longer than [count] an exception will be thrown when `length > count`. * If [replacement] is longer than [count] an exception will be thrown when `length > count`.
*/ */
fun String.chop(count: Int, replacement: String = ""): String { fun String.chop(
count: Int,
replacement: String = "",
): String {
return if (length > count) { return if (length > count) {
take(count - replacement.length) + replacement take(count - replacement.length) + replacement
} else { } else {

View File

@@ -16,46 +16,63 @@ import kotlinx.serialization.modules.SerializersModule
class LazyPreferenceStore( class LazyPreferenceStore(
private val lazyStore: Lazy<PreferenceStore>, private val lazyStore: Lazy<PreferenceStore>,
) : PreferenceStore { ) : PreferenceStore {
/** /**
* Returns an [String] preference for this [key]. * Returns an [String] preference for this [key].
*/ */
override fun getString(key: String, defaultValue: String): Preference<String> { override fun getString(
key: String,
defaultValue: String,
): Preference<String> {
return 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].
*/ */
override fun getLong(key: String, defaultValue: Long): Preference<Long> { override fun getLong(
key: String,
defaultValue: Long,
): Preference<Long> {
return 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].
*/ */
override fun getInt(key: String, defaultValue: Int): Preference<Int> { override fun getInt(
key: String,
defaultValue: Int,
): Preference<Int> {
return 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].
*/ */
override fun getFloat(key: String, defaultValue: Float): Preference<Float> { override fun getFloat(
key: String,
defaultValue: Float,
): Preference<Float> {
return 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].
*/ */
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> { override fun getBoolean(
key: String,
defaultValue: Boolean,
): Preference<Boolean> {
return 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].
*/ */
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> { override fun getStringSet(
key: String,
defaultValue: Set<String>,
): Preference<Set<String>> {
return lazyStore.value.getStringSet(key, defaultValue) return lazyStore.value.getStringSet(key, defaultValue)
} }

View File

@@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.onStart
* this interface must be provided through a [PreferenceStore]. * this interface must be provided through a [PreferenceStore].
*/ */
interface Preference<T> { interface Preference<T> {
/** /**
* Returns the key of this preference. * Returns the key of this preference.
*/ */

View File

@@ -15,36 +15,53 @@ import kotlinx.serialization.modules.SerializersModule
* persist these preferences on disk. * persist these preferences on disk.
*/ */
interface PreferenceStore { interface PreferenceStore {
/** /**
* Returns an [String] preference for this [key]. * Returns an [String] preference for this [key].
*/ */
fun getString(key: String, defaultValue: String = ""): Preference<String> fun getString(
key: String,
defaultValue: String = "",
): Preference<String>
/** /**
* Returns a [Long] preference for this [key]. * Returns a [Long] preference for this [key].
*/ */
fun getLong(key: String, defaultValue: Long = 0): Preference<Long> fun getLong(
key: String,
defaultValue: Long = 0,
): Preference<Long>
/** /**
* Returns an [Int] preference for this [key]. * Returns an [Int] preference for this [key].
*/ */
fun getInt(key: String, defaultValue: Int = 0): Preference<Int> fun getInt(
key: String,
defaultValue: Int = 0,
): Preference<Int>
/** /**
* Returns a [Float] preference for this [key]. * Returns a [Float] preference for this [key].
*/ */
fun getFloat(key: String, defaultValue: Float = 0f): Preference<Float> fun getFloat(
key: String,
defaultValue: Float = 0f,
): Preference<Float>
/** /**
* Returns a [Boolean] preference for this [key]. * Returns a [Boolean] preference for this [key].
*/ */
fun getBoolean(key: String, defaultValue: Boolean = false): Preference<Boolean> fun getBoolean(
key: String,
defaultValue: Boolean = false,
): Preference<Boolean>
/** /**
* Returns a [Set<String>] preference for this [key]. * Returns a [Set<String>] preference for this [key].
*/ */
fun getStringSet(key: String, defaultValue: Set<String> = emptySet()): Preference<Set<String>> fun getStringSet(
key: String,
defaultValue: Set<String> = emptySet(),
): Preference<Set<String>>
/** /**
* 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

View File

@@ -17,81 +17,150 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
interface Adapter<T> { interface Adapter<T> {
fun get(key: String, preferences: ObservableSettings): T fun get(
key: String,
preferences: ObservableSettings,
): T
fun set(key: String, value: T, editor: ObservableSettings) fun set(
key: String,
value: T,
editor: ObservableSettings,
)
fun isSet(keys: Set<String>, key: String): Boolean = key in keys fun isSet(
keys: Set<String>,
key: String,
): Boolean = key in keys
fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener
} }
internal object StringAdapter : Adapter<String> { internal object StringAdapter : Adapter<String> {
override fun get(key: String, preferences: ObservableSettings): String { override fun get(
key: String,
preferences: ObservableSettings,
): String {
return preferences.getString(key, "") // Not called unless key is present. return preferences.getString(key, "") // Not called unless key is present.
} }
override fun set(key: String, value: String, editor: ObservableSettings) { override fun set(
key: String,
value: String,
editor: ObservableSettings,
) {
editor.putString(key, value) editor.putString(key, value)
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addStringOrNullListener(key) { callback() } return preferences.addStringOrNullListener(key) { callback() }
} }
} }
internal object LongAdapter : Adapter<Long> { internal object LongAdapter : Adapter<Long> {
override fun get(key: String, preferences: ObservableSettings): Long { override fun get(
key: String,
preferences: ObservableSettings,
): Long {
return preferences.getLong(key, 0) return preferences.getLong(key, 0)
} }
override fun set(key: String, value: Long, editor: ObservableSettings) { override fun set(
key: String,
value: Long,
editor: ObservableSettings,
) {
editor.putLong(key, value) editor.putLong(key, value)
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addLongOrNullListener(key) { callback() } return preferences.addLongOrNullListener(key) { callback() }
} }
} }
internal object IntAdapter : Adapter<Int> { internal object IntAdapter : Adapter<Int> {
override fun get(key: String, preferences: ObservableSettings): Int { override fun get(
key: String,
preferences: ObservableSettings,
): Int {
return preferences.getInt(key, 0) return preferences.getInt(key, 0)
} }
override fun set(key: String, value: Int, editor: ObservableSettings) { override fun set(
key: String,
value: Int,
editor: ObservableSettings,
) {
editor.putInt(key, value) editor.putInt(key, value)
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addIntOrNullListener(key) { callback() } return preferences.addIntOrNullListener(key) { callback() }
} }
} }
internal object FloatAdapter : Adapter<Float> { internal object FloatAdapter : Adapter<Float> {
override fun get(key: String, preferences: ObservableSettings): Float { override fun get(
key: String,
preferences: ObservableSettings,
): Float {
return preferences.getFloat(key, 0f) return preferences.getFloat(key, 0f)
} }
override fun set(key: String, value: Float, editor: ObservableSettings) { override fun set(
key: String,
value: Float,
editor: ObservableSettings,
) {
editor.putFloat(key, value) editor.putFloat(key, value)
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addFloatOrNullListener(key) { callback() } return preferences.addFloatOrNullListener(key) { callback() }
} }
} }
internal object BooleanAdapter : Adapter<Boolean> { internal object BooleanAdapter : Adapter<Boolean> {
override fun get(key: String, preferences: ObservableSettings): Boolean { override fun get(
key: String,
preferences: ObservableSettings,
): Boolean {
return preferences.getBoolean(key, false) return preferences.getBoolean(key, false)
} }
override fun set(key: String, value: Boolean, editor: ObservableSettings) { override fun set(
key: String,
value: Boolean,
editor: ObservableSettings,
) {
editor.putBoolean(key, value) editor.putBoolean(key, value)
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addBooleanOrNullListener(key) { callback() } return preferences.addBooleanOrNullListener(key) { callback() }
} }
} }
@@ -99,18 +168,28 @@ internal object BooleanAdapter : Adapter<Boolean> {
internal object StringSetAdapter : Adapter<Set<String>> { internal object StringSetAdapter : Adapter<Set<String>> {
private val serializer = SetSerializer(String.serializer()) private val serializer = SetSerializer(String.serializer())
override fun get(key: String, preferences: ObservableSettings): Set<String> { override fun get(
key: String,
preferences: ObservableSettings,
): Set<String> {
return preferences.decodeValue(serializer, key, emptySet()) // Not called unless key is present. return preferences.decodeValue(serializer, key, emptySet()) // Not called unless key is present.
} }
override fun set(key: String, value: Set<String>, editor: ObservableSettings) { override fun set(
key: String,
value: Set<String>,
editor: ObservableSettings,
) {
editor.encodeValue(serializer, key, value) editor.encodeValue(serializer, key, value)
} }
/** /**
* Encoding a string set makes a list of keys and a size key, such as key.size and key.0-size * Encoding a string set makes a list of keys and a size key, such as key.size and key.0-size
*/ */
override fun isSet(keys: Set<String>, key: String): Boolean { override fun isSet(
keys: Set<String>,
key: String,
): Boolean {
return keys.contains("$key.size") return keys.contains("$key.size")
} }
@@ -118,7 +197,11 @@ internal object StringSetAdapter : Adapter<Set<String>> {
* Watching the regular key doesn't produce updates for a string set for some reason * Watching the regular key doesn't produce updates for a string set for some reason
* TODO make better, doesn't produce updates when you add something and remove something * TODO make better, doesn't produce updates when you add something and remove something
*/ */
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addIntOrNullListener("$key.size") { callback() } return preferences.addIntOrNullListener("$key.size") { callback() }
} }
} }
@@ -127,16 +210,26 @@ internal class ObjectAdapter<T>(
private val serializer: (T) -> String, private val serializer: (T) -> String,
private val deserializer: (String) -> T, private val deserializer: (String) -> T,
) : Adapter<T> { ) : Adapter<T> {
override fun get(
override fun get(key: String, preferences: ObservableSettings): T { key: String,
preferences: ObservableSettings,
): T {
return deserializer(preferences.getString(key, "")) // Not called unless key is present. return deserializer(preferences.getString(key, "")) // Not called unless key is present.
} }
override fun set(key: String, value: T, editor: ObservableSettings) { override fun set(
key: String,
value: T,
editor: ObservableSettings,
) {
editor.putString(key, serializer(value)) editor.putString(key, serializer(value))
} }
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
return preferences.addStringOrNullListener(key) { callback() } return preferences.addStringOrNullListener(key) { callback() }
} }
} }
@@ -146,12 +239,18 @@ internal class JsonObjectAdapter<T>(
private val serializer: KSerializer<T>, private val serializer: KSerializer<T>,
private val serializersModule: SerializersModule = EmptySerializersModule(), private val serializersModule: SerializersModule = EmptySerializersModule(),
) : Adapter<T> { ) : Adapter<T> {
override fun get(
override fun get(key: String, preferences: ObservableSettings): T { key: String,
preferences: ObservableSettings,
): T {
return preferences.decodeValue(serializer, key, defaultValue, serializersModule) // Not called unless key is present. return preferences.decodeValue(serializer, key, defaultValue, serializersModule) // Not called unless key is present.
} }
override fun set(key: String, value: T, editor: ObservableSettings) { override fun set(
key: String,
value: T,
editor: ObservableSettings,
) {
editor.encodeValue(serializer, key, value, serializersModule) editor.encodeValue(serializer, key, value, serializersModule)
} }
@@ -159,14 +258,21 @@ internal class JsonObjectAdapter<T>(
* Encoding a structure makes keys start with the [key] and adds extensions for values, * Encoding a structure makes keys start with the [key] and adds extensions for values,
* for a pair it would be like [key].first [key].second. * for a pair it would be like [key].first [key].second.
*/ */
override fun isSet(keys: Set<String>, key: String): Boolean { override fun isSet(
keys: Set<String>,
key: String,
): Boolean {
return keys.any { it.startsWith(key) } return keys.any { it.startsWith(key) }
} }
/** /**
* Todo doesn't work * Todo doesn't work
*/ */
override fun addListener(key: String, preferences: ObservableSettings, callback: () -> Unit): SettingsListener { override fun addListener(
key: String,
preferences: ObservableSettings,
callback: () -> Unit,
): SettingsListener {
@Suppress("DEPRECATION") // Because we don't care about the type, and it crashes with any other listener @Suppress("DEPRECATION") // Because we don't care about the type, and it crashes with any other listener
return preferences.addListener(key) { callback() } return preferences.addListener(key) { callback() }
} }

View File

@@ -21,7 +21,6 @@ internal class StandardPreference<T>(
private val defaultValue: T, private val defaultValue: T,
private val adapter: Adapter<T>, private val adapter: Adapter<T>,
) : Preference<T> { ) : Preference<T> {
/** /**
* Returns the key of this preference. * Returns the key of this preference.
*/ */

View File

@@ -11,46 +11,63 @@ 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].
*/ */
override fun getString(key: String, defaultValue: String): Preference<String> { override fun getString(
key: String,
defaultValue: String,
): Preference<String> {
return StandardPreference(preferences, key, defaultValue, StringAdapter) return StandardPreference(preferences, key, defaultValue, StringAdapter)
} }
/** /**
* Returns a [Long] preference for this [key]. * Returns a [Long] preference for this [key].
*/ */
override fun getLong(key: String, defaultValue: Long): Preference<Long> { override fun getLong(
key: String,
defaultValue: Long,
): Preference<Long> {
return StandardPreference(preferences, key, defaultValue, LongAdapter) return StandardPreference(preferences, key, defaultValue, LongAdapter)
} }
/** /**
* Returns an [Int] preference for this [key]. * Returns an [Int] preference for this [key].
*/ */
override fun getInt(key: String, defaultValue: Int): Preference<Int> { override fun getInt(
key: String,
defaultValue: Int,
): Preference<Int> {
return StandardPreference(preferences, key, defaultValue, IntAdapter) return StandardPreference(preferences, key, defaultValue, IntAdapter)
} }
/** /**
* Returns a [Float] preference for this [key]. * Returns a [Float] preference for this [key].
*/ */
override fun getFloat(key: String, defaultValue: Float): Preference<Float> { override fun getFloat(
key: String,
defaultValue: Float,
): Preference<Float> {
return StandardPreference(preferences, key, defaultValue, FloatAdapter) return StandardPreference(preferences, key, defaultValue, FloatAdapter)
} }
/** /**
* Returns a [Boolean] preference for this [key]. * Returns a [Boolean] preference for this [key].
*/ */
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> { override fun getBoolean(
key: String,
defaultValue: Boolean,
): Preference<Boolean> {
return StandardPreference(preferences, key, defaultValue, BooleanAdapter) return StandardPreference(preferences, key, defaultValue, BooleanAdapter)
} }
/** /**
* Returns a [Set<String>] preference for this [key]. * Returns a [Set<String>] preference for this [key].
*/ */
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> { override fun getStringSet(
key: String,
defaultValue: Set<String>,
): Preference<Set<String>> {
return StandardPreference(preferences, key, defaultValue, StringSetAdapter) return StandardPreference(preferences, key, defaultValue, StringSetAdapter)
} }

View File

@@ -9,7 +9,10 @@ package ca.gosyer.jui.core.util
/** /**
* Returns a new list that replaces the item at the given [position] with [newItem]. * Returns a new list that replaces the item at the given [position] with [newItem].
*/ */
fun <T> List<T>.replace(position: Int, newItem: T): List<T> { fun <T> List<T>.replace(
position: Int,
newItem: T,
): List<T> {
val newList = toMutableList() val newList = toMutableList()
newList[position] = newItem newList[position] = newItem
return newList return newList
@@ -19,7 +22,10 @@ fun <T> List<T>.replace(position: Int, newItem: T): List<T> {
* Returns a new list that replaces the first occurrence that matches the given [predicate] with * Returns a new list that replaces the first occurrence that matches the given [predicate] with
* [newItem]. If no item matches the predicate, the same list is returned (and unmodified). * [newItem]. If no item matches the predicate, the same list is returned (and unmodified).
*/ */
inline fun <T> List<T>.replaceFirst(predicate: (T) -> Boolean, newItem: T): List<T> { inline fun <T> List<T>.replaceFirst(
predicate: (T) -> Boolean,
newItem: T,
): List<T> {
forEachIndexed { index, element -> forEachIndexed { index, element ->
if (predicate(element)) { if (predicate(element)) {
return replace(index, newItem) return replace(index, newItem)

View File

@@ -9,7 +9,6 @@ package ca.gosyer.jui.core.util
import io.ktor.utils.io.core.toByteArray import io.ktor.utils.io.core.toByteArray
object ImageUtil { object ImageUtil {
private val jpgMagic = charByteArrayOf(0xFF, 0xD8, 0xFF) private val jpgMagic = charByteArrayOf(0xFF, 0xD8, 0xFF)
private val pngMagic = charByteArrayOf(0x89, 0x50, 0x4E, 0x47) private val pngMagic = charByteArrayOf(0x89, 0x50, 0x4E, 0x47)
private val gifMagic = "GIF8".toByteArray() private val gifMagic = "GIF8".toByteArray()

View File

@@ -10,7 +10,9 @@ import com.russhwolf.settings.PreferencesSettings
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import java.util.prefs.Preferences import java.util.prefs.Preferences
actual class PreferenceStoreFactory @Inject constructor() { actual class PreferenceStoreFactory
@Inject
constructor() {
private val rootNode: Preferences = Preferences.userRoot() private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui") .node("ca/gosyer/tachideskjui")
@@ -21,4 +23,4 @@ actual class PreferenceStoreFactory @Inject constructor() {
), ),
) )
} }
} }

View File

@@ -17,14 +17,16 @@ fun Locale.toPlatform(): PlatformLocale = PlatformLocale(toLanguageTag())
* First Locale: en_IN * First Locale: en_IN
* Language: English * Language: English
*/ */
actual fun Locale.getDisplayLanguage(displayLocale: Locale): String = toPlatform() actual fun Locale.getDisplayLanguage(displayLocale: Locale): String =
toPlatform()
.localizedStringForLanguageCode(displayLocale.toLanguageTag())!! .localizedStringForLanguageCode(displayLocale.toLanguageTag())!!
/** /**
* First Locale: en_US * First Locale: en_US
* Language: English (United States) * Language: English (United States)
*/ */
actual fun Locale.getDisplayName(displayLocale: Locale): String = toPlatform() actual fun Locale.getDisplayName(displayLocale: Locale): String =
toPlatform()
.localizedStringForLocaleIdentifier(displayLocale.toLanguageTag()) .localizedStringForLocaleIdentifier(displayLocale.toLanguageTag())
actual val Locale.displayName: String get() = getDisplayLanguage(this) actual val Locale.displayName: String get() = getDisplayLanguage(this)

View File

@@ -10,7 +10,9 @@ import com.russhwolf.settings.NSUserDefaultsSettings
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import platform.Foundation.NSUserDefaults import platform.Foundation.NSUserDefaults
actual class PreferenceStoreFactory @Inject constructor() { actual class PreferenceStoreFactory
@Inject
constructor() {
actual fun create(vararg names: String): PreferenceStore { actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore( return StandardPreferenceStore(
NSUserDefaultsSettings( NSUserDefaultsSettings(
@@ -18,4 +20,4 @@ actual class PreferenceStoreFactory @Inject constructor() {
), ),
) )
} }
} }

View File

@@ -11,10 +11,12 @@ import java.util.Locale as PlatformLocale
fun Locale.toPlatform(): PlatformLocale = PlatformLocale.forLanguageTag(toLanguageTag()) fun Locale.toPlatform(): PlatformLocale = PlatformLocale.forLanguageTag(toLanguageTag())
actual fun Locale.getDisplayLanguage(displayLocale: Locale): String = toPlatform() actual fun Locale.getDisplayLanguage(displayLocale: Locale): String =
toPlatform()
.getDisplayLanguage(displayLocale.toPlatform()) .getDisplayLanguage(displayLocale.toPlatform())
actual fun Locale.getDisplayName(displayLocale: Locale): String = toPlatform() actual fun Locale.getDisplayName(displayLocale: Locale): String =
toPlatform()
.getDisplayName(displayLocale.toPlatform()) .getDisplayName(displayLocale.toPlatform())
actual val Locale.displayName: String get() = toPlatform().displayName actual val Locale.displayName: String get() = toPlatform().displayName

View File

@@ -24,9 +24,11 @@ import de.jensklingenberg.ktorfit.Ktorfit
import me.tatarka.inject.annotations.Provides import me.tatarka.inject.annotations.Provides
interface DataComponent { interface DataComponent {
@Provides @Provides
fun ktorfit(http: Http, serverPreferences: ServerPreferences) = Ktorfit fun ktorfit(
http: Http,
serverPreferences: ServerPreferences,
) = Ktorfit
.Builder() .Builder()
.httpClient(http) .httpClient(http)
.converterFactories(FlowConverterFactory()) .converterFactories(FlowConverterFactory())

View File

@@ -18,19 +18,17 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
class FlowConverterFactory : Converter.Factory { class FlowConverterFactory : Converter.Factory {
private class FlowResponseConverter( private class FlowResponseConverter(
val typeData: TypeData, val typeData: TypeData,
val ktorfit: Ktorfit val ktorfit: Ktorfit,
) : Converter.ResponseConverter<HttpResponse, Flow<Any?>> { ) : Converter.ResponseConverter<HttpResponse, Flow<Any?>> {
override fun convert(getResponse: suspend () -> HttpResponse): Flow<Any?> { override fun convert(getResponse: suspend () -> HttpResponse): Flow<Any?> {
return flow { return flow {
val response = getResponse() val response = getResponse()
val convertedBody = ktorfit.nextSuspendResponseConverter( val convertedBody = ktorfit.nextSuspendResponseConverter(
null, null,
typeData.typeArgs.first() typeData.typeArgs.first(),
)?.convert(response) )?.convert(response)
?: response.body(typeData.typeArgs.first().typeInfo) ?: response.body(typeData.typeArgs.first().typeInfo)
emit(convertedBody) emit(convertedBody)
@@ -40,7 +38,7 @@ class FlowConverterFactory : Converter.Factory {
override fun responseConverter( override fun responseConverter(
typeData: TypeData, typeData: TypeData,
ktorfit: Ktorfit ktorfit: Ktorfit,
): Converter.ResponseConverter<HttpResponse, *>? { ): Converter.ResponseConverter<HttpResponse, *>? {
if (typeData.typeInfo.type == Flow::class) { if (typeData.typeInfo.type == Flow::class) {
return FlowResponseConverter(typeData, ktorfit) return FlowResponseConverter(typeData, ktorfit)
@@ -50,9 +48,8 @@ class FlowConverterFactory : Converter.Factory {
override fun suspendResponseConverter( override fun suspendResponseConverter(
typeData: TypeData, typeData: TypeData,
ktorfit: Ktorfit ktorfit: Ktorfit,
): Converter.SuspendResponseConverter<HttpResponse, *>? { ): Converter.SuspendResponseConverter<HttpResponse, *>? {
return null return null
} }
} }

View File

@@ -15,7 +15,9 @@ import platform.Foundation.NSDateFormatter
import platform.Foundation.NSDateFormatterNoStyle import platform.Foundation.NSDateFormatterNoStyle
import platform.Foundation.NSDateFormatterShortStyle import platform.Foundation.NSDateFormatterShortStyle
actual class DateHandler @Inject constructor() { actual class DateHandler
@Inject
constructor() {
actual val formatOptions by lazy { actual val formatOptions by lazy {
listOf( listOf(
"", "",
@@ -25,7 +27,8 @@ actual class DateHandler @Inject constructor() {
) )
} }
actual fun getDateFormat(format: String): (Instant) -> String = when (format) { actual fun getDateFormat(format: String): (Instant) -> String =
when (format) {
"" -> NSDateFormatter() "" -> NSDateFormatter()
.apply { .apply {
setDateStyle(NSDateFormatterShortStyle) setDateStyle(NSDateFormatterShortStyle)
@@ -55,4 +58,4 @@ actual class DateHandler @Inject constructor() {
} }
} }
} }
} }

View File

@@ -15,7 +15,9 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
actual class DateHandler @Inject constructor() { actual class DateHandler
@Inject
constructor() {
actual val formatOptions by lazy { actual val formatOptions by lazy {
listOf( listOf(
"", "",
@@ -25,7 +27,8 @@ actual class DateHandler @Inject constructor() {
) )
} }
actual fun getDateFormat(format: String): (Instant) -> String = when (format) { actual fun getDateFormat(format: String): (Instant) -> String =
when (format) {
"" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) "" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.current.toPlatform()) .withLocale(Locale.current.toPlatform())
.withZone(ZoneId.systemDefault()) .withZone(ZoneId.systemDefault())
@@ -48,4 +51,4 @@ actual class DateHandler @Inject constructor() {
} }
} }
} }
} }

View File

@@ -21,7 +21,6 @@ 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
@@ -35,7 +34,8 @@ abstract class AppComponent(
companion object { companion object {
private var appComponentInstance: AppComponent? = null private var appComponentInstance: AppComponent? = null
fun getInstance(context: ContextWrapper) = appComponentInstance ?: create(context) fun getInstance(context: ContextWrapper) =
appComponentInstance ?: create(context)
.also { appComponentInstance = it } .also { appComponentInstance = it }
} }
} }

View File

@@ -11,11 +11,12 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.ContextWrapper
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
class AppMigrations @Inject constructor( class AppMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences, private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper, private val contextWrapper: ContextWrapper,
) { ) {
fun runMigrations(): Boolean { fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get() val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.MIGRATION_CODE) { if (oldVersion < BuildConfig.MIGRATION_CODE) {
@@ -29,4 +30,4 @@ class AppMigrations @Inject constructor(
} }
return false return false
} }
} }

View File

@@ -12,32 +12,46 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
class Slf4jLogFactory : LogFactory { class Slf4jLogFactory : LogFactory {
override fun createKmLog(
override fun createKmLog(tag: String, className: String): KmLog { tag: String,
className: String,
): KmLog {
return 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(tag: String, msg: String) { override fun verbose(
tag: String,
msg: String,
) {
super.verbose(tag, msg) super.verbose(tag, msg)
logger.trace(msg) logger.trace(msg)
} }
override fun debug(tag: String, msg: String) { override fun debug(
tag: String,
msg: String,
) {
super.debug(tag, msg) super.debug(tag, msg)
logger.debug(msg) logger.debug(msg)
} }
override fun info(tag: String, msg: String) { override fun info(
tag: String,
msg: String,
) {
super.info(tag, msg) super.info(tag, msg)
logger.info(msg) logger.info(msg)
} }
override fun warn(tag: String, msg: String, t: Throwable?) { override fun warn(
tag: String,
msg: String,
t: Throwable?,
) {
super.warn(tag, msg, t) super.warn(tag, msg, t)
if (t != null) { if (t != null) {
logger.warn(msg, t) logger.warn(msg, t)
@@ -46,7 +60,11 @@ class Slf4jLog(tag: String) : KmLog(tag) {
} }
} }
override fun error(tag: String, msg: String, t: Throwable?) { override fun error(
tag: String,
msg: String,
t: Throwable?,
) {
super.error(tag, msg, t) super.error(tag, msg, t)
if (t != null) { if (t != null) {
logger.error(msg, t) logger.error(msg, t)

View File

@@ -253,7 +253,10 @@ suspend fun main() {
} }
@Composable @Composable
fun ToastOverlay(modifier: Modifier, context: ContextWrapper) { fun ToastOverlay(
modifier: Modifier,
context: ContextWrapper,
) {
var toast by remember { mutableStateOf<Pair<String, Length>?>(null) } var toast by remember { mutableStateOf<Pair<String, Length>?>(null) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
context.toasts context.toasts

View File

@@ -23,7 +23,9 @@ import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ServerListeners @Inject constructor() { class ServerListeners
@Inject
constructor() {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private fun <T> Flow<T>.startWith(value: T) = onStart { emit(value) } private fun <T> Flow<T>.startWith(value: T) = onStart { emit(value) }
@@ -51,8 +53,10 @@ class ServerListeners @Inject constructor() {
extraBufferCapacity = Channel.UNLIMITED, extraBufferCapacity = Channel.UNLIMITED,
) )
fun <T> combineMangaUpdates(flow: Flow<T>, predate: (suspend (List<Long>) -> Boolean)? = null) = fun <T> combineMangaUpdates(
if (predate != null) { flow: Flow<T>,
predate: (suspend (List<Long>) -> Boolean)? = null,
) = if (predate != null) {
_mangaListener _mangaListener
.filter(predate) .filter(predate)
.startWith(Unit) .startWith(Unit)
@@ -68,8 +72,10 @@ class ServerListeners @Inject constructor() {
} }
} }
fun <T> combineCategoryManga(flow: Flow<T>, predate: (suspend (Long) -> Boolean)? = null) = fun <T> combineCategoryManga(
if (predate != null) { flow: Flow<T>,
predate: (suspend (Long) -> Boolean)? = null,
) = if (predate != null) {
categoryMangaListener.filter(predate).startWith(-1) categoryMangaListener.filter(predate).startWith(-1)
} else { } else {
categoryMangaListener.startWith(-1) categoryMangaListener.startWith(-1)
@@ -104,25 +110,37 @@ class ServerListeners @Inject constructor() {
.flatMapLatest { flow } .flatMapLatest { flow }
} }
fun updateChapters(mangaId: Long, chapterIndexes: List<Int>) { fun updateChapters(
mangaId: Long,
chapterIndexes: List<Int>,
) {
scope.launch { scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.ifEmpty { null }) _chapterIndexesListener.emit(mangaId to chapterIndexes.ifEmpty { null })
} }
} }
fun updateChapters(mangaId: Long, vararg chapterIndexes: Int) { fun updateChapters(
mangaId: Long,
vararg chapterIndexes: Int,
) {
scope.launch { scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.toList().ifEmpty { null }) _chapterIndexesListener.emit(mangaId to chapterIndexes.toList().ifEmpty { null })
} }
} }
fun updateChapters(mangaId: Long?, chapterIds: List<Long>) { fun updateChapters(
mangaId: Long?,
chapterIds: List<Long>,
) {
scope.launch { scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds) _chapterIdsListener.emit(mangaId to chapterIds)
} }
} }
fun updateChapters(mangaId: Long?, vararg chapterIds: Long) { fun updateChapters(
mangaId: Long?,
vararg chapterIds: Long,
) {
scope.launch { scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds.toList()) _chapterIdsListener.emit(mangaId to chapterIds.toList())
} }
@@ -131,4 +149,4 @@ class ServerListeners @Inject constructor() {
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -60,8 +60,10 @@ interface SharedDomainComponent : CoreComponent {
@AppScope @AppScope
@Provides @Provides
fun httpFactory(serverPreferences: ServerPreferences, json: Json) = fun httpFactory(
httpClient(serverPreferences, json) serverPreferences: ServerPreferences,
json: Json,
) = httpClient(serverPreferences, json)
@get:AppScope @get:AppScope
@get:Provides @get:Provides

View File

@@ -13,19 +13,22 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ExportBackupFile @Inject constructor(private val backupRepository: BackupRepository) { class ExportBackupFile
@Inject
suspend fun await(block: HttpRequestBuilder.() -> Unit = {}, onError: suspend (Throwable) -> Unit = {}) = asFlow(block) constructor(private val backupRepository: BackupRepository) {
suspend fun await(
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},
) = asFlow(block)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to export backup" } log.warn(it) { "Failed to export backup" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(block: HttpRequestBuilder.() -> Unit = {}) = fun asFlow(block: HttpRequestBuilder.() -> Unit = {}) = backupRepository.exportBackupFile(block)
backupRepository.exportBackupFile(block)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,19 +14,26 @@ import me.tatarka.inject.annotations.Inject
import okio.Path import okio.Path
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ImportBackupFile @Inject constructor(private val backupRepository: BackupRepository) { class ImportBackupFile
@Inject
suspend fun await(file: Path, block: HttpRequestBuilder.() -> Unit = {}, onError: suspend (Throwable) -> Unit = {}) = asFlow(file, block) constructor(private val backupRepository: BackupRepository) {
suspend fun await(
file: Path,
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file, block)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to import backup ${file.name}" } log.warn(it) { "Failed to import backup ${file.name}" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = fun asFlow(
backupRepository.importBackupFile(BackupRepository.buildBackupFormData(file), block) file: Path,
block: HttpRequestBuilder.() -> Unit = {},
) = backupRepository.importBackupFile(BackupRepository.buildBackupFormData(file), block)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,19 +14,26 @@ import me.tatarka.inject.annotations.Inject
import okio.Path import okio.Path
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ValidateBackupFile @Inject constructor(private val backupRepository: BackupRepository) { class ValidateBackupFile
@Inject
suspend fun await(file: Path, block: HttpRequestBuilder.() -> Unit = {}, onError: suspend (Throwable) -> Unit = {}) = asFlow(file, block) constructor(private val backupRepository: BackupRepository) {
suspend fun await(
file: Path,
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file, block)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to validate backup ${file.name}" } log.warn(it) { "Failed to validate backup ${file.name}" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = fun asFlow(
backupRepository.validateBackupFile(BackupRepository.buildBackupFormData(file), block) file: Path,
block: HttpRequestBuilder.() -> Unit = {},
) = backupRepository.validateBackupFile(BackupRepository.buildBackupFormData(file), block)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -26,7 +26,6 @@ import okio.Path
import okio.buffer import okio.buffer
interface BackupRepository { interface BackupRepository {
@Multipart @Multipart
@POST("api/v1/backup/import/file") @POST("api/v1/backup/import/file")
fun importBackupFile( fun importBackupFile(
@@ -47,7 +46,8 @@ interface BackupRepository {
): Flow<HttpResponse> ): Flow<HttpResponse>
companion object { companion object {
fun buildBackupFormData(file: Path) = formData { fun buildBackupFormData(file: Path) =
formData {
append( append(
"backup.proto.gz", "backup.proto.gz",
FileSystem.SYSTEM.source(file).buffer().readByteArray(), FileSystem.SYSTEM.source(file).buffer().readByteArray(),

View File

@@ -17,26 +17,38 @@ import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class AddMangaToCategory @Inject constructor( class AddMangaToCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, categoryId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, categoryId) mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to add $mangaId to category $categoryId" } log.warn(it) { "Failed to add $mangaId to category $categoryId" }
} }
.collect() .collect()
suspend fun await(manga: Manga, category: Category, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, category) suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to category ${category.name}" } log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to category ${category.name}" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, categoryId: Long) = if (categoryId != 0L) { fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.addMangaToCategory(mangaId, categoryId) categoryRepository.addMangaToCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) } .map { serverListeners.updateCategoryManga(categoryId) }
} else { } else {
@@ -46,7 +58,10 @@ class AddMangaToCategory @Inject constructor(
} }
} }
fun asFlow(manga: Manga, category: Category) = if (category.id != 0L) { fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.addMangaToCategory(manga.id, category.id) categoryRepository.addMangaToCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) } .map { serverListeners.updateCategoryManga(category.id) }
} else { } else {
@@ -59,4 +74,4 @@ class AddMangaToCategory @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,9 +12,13 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class CreateCategory @Inject constructor(private val categoryRepository: CategoryRepository) { class CreateCategory
@Inject
suspend fun await(name: String, onError: suspend (Throwable) -> Unit = {}) = asFlow(name) constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(name)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to create category $name" } log.warn(it) { "Failed to create category $name" }
@@ -26,4 +30,4 @@ class CreateCategory @Inject constructor(private val categoryRepository: Categor
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,16 +13,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class DeleteCategory @Inject constructor(private val categoryRepository: CategoryRepository) { class DeleteCategory
@Inject
suspend fun await(categoryId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(categoryId) constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to delete category $categoryId" } log.warn(it) { "Failed to delete category $categoryId" }
} }
.collect() .collect()
suspend fun await(category: Category, onError: suspend (Throwable) -> Unit = {}) = asFlow(category) suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to delete category ${category.name}" } log.warn(it) { "Failed to delete category ${category.name}" }
@@ -36,4 +43,4 @@ class DeleteCategory @Inject constructor(private val categoryRepository: Categor
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,16 +13,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetCategories @Inject constructor(private val categoryRepository: CategoryRepository) { class GetCategories
@Inject
suspend fun await(dropDefault: Boolean = false, onError: suspend (Throwable) -> Unit = {}) = asFlow(dropDefault) constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
dropDefault: Boolean = false,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(dropDefault)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get categories" } log.warn(it) { "Failed to get categories" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(dropDefault: Boolean = false) = categoryRepository.getCategories() fun asFlow(dropDefault: Boolean = false) =
categoryRepository.getCategories()
.map { categories -> .map { categories ->
if (dropDefault) { if (dropDefault) {
categories.filterNot { it.name.equals("default", true) } categories.filterNot { it.name.equals("default", true) }
@@ -34,4 +39,4 @@ class GetCategories @Inject constructor(private val categoryRepository: Category
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,16 +13,23 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetMangaCategories @Inject constructor(private val categoryRepository: CategoryRepository) { class GetMangaCategories
@Inject
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get categories for $mangaId" } log.warn(it) { "Failed to get categories for $mangaId" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get categories for ${manga.title}(${manga.id})" } log.warn(it) { "Failed to get categories for ${manga.title}(${manga.id})" }
@@ -36,4 +43,4 @@ class GetMangaCategories @Inject constructor(private val categoryRepository: Cat
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,12 +15,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetMangaListFromCategory @Inject constructor( class GetMangaListFromCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(categoryId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(categoryId) categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -28,7 +32,10 @@ class GetMangaListFromCategory @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(category: Category, onError: suspend (Throwable) -> Unit = {}) = asFlow(category) suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -36,15 +43,17 @@ class GetMangaListFromCategory @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(categoryId: Long) = serverListeners.combineCategoryManga( fun asFlow(categoryId: Long) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(categoryId), categoryRepository.getMangaFromCategory(categoryId),
) { categoryId == it } ) { categoryId == it }
fun asFlow(category: Category) = serverListeners.combineCategoryManga( fun asFlow(category: Category) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(category.id), categoryRepository.getMangaFromCategory(category.id),
) { category.id == it } ) { category.id == it }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,9 +13,14 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ModifyCategory @Inject constructor(private val categoryRepository: CategoryRepository) { class ModifyCategory
@Inject
suspend fun await(categoryId: Long, name: String, onError: suspend (Throwable) -> Unit = {}) = asFlow( constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
categoryId: Long,
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
categoryId = categoryId, categoryId = categoryId,
name = name, name = name,
).catch { ).catch {
@@ -23,7 +28,11 @@ class ModifyCategory @Inject constructor(private val categoryRepository: Categor
log.warn(it) { "Failed to modify category $categoryId with options: name=$name" } log.warn(it) { "Failed to modify category $categoryId with options: name=$name" }
}.collect() }.collect()
suspend fun await(category: Category, name: String? = null, onError: suspend (Throwable) -> Unit = {}) = asFlow( suspend fun await(
category: Category,
name: String? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
category = category, category = category,
name = name, name = name,
).catch { ).catch {
@@ -31,12 +40,18 @@ class ModifyCategory @Inject constructor(private val categoryRepository: Categor
log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" } log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" }
}.collect() }.collect()
fun asFlow(categoryId: Long, name: String) = categoryRepository.modifyCategory( fun asFlow(
categoryId: Long,
name: String,
) = categoryRepository.modifyCategory(
categoryId = categoryId, categoryId = categoryId,
name = name, name = name,
) )
fun asFlow(category: Category, name: String? = null) = categoryRepository.modifyCategory( fun asFlow(
category: Category,
name: String? = null,
) = categoryRepository.modifyCategory(
categoryId = category.id, categoryId = category.id,
name = name ?: category.name, name = name ?: category.name,
) )
@@ -44,4 +59,4 @@ class ModifyCategory @Inject constructor(private val categoryRepository: Categor
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -17,26 +17,38 @@ import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class RemoveMangaFromCategory @Inject constructor( class RemoveMangaFromCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, categoryId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, categoryId) mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to remove $mangaId from category $categoryId" } log.warn(it) { "Failed to remove $mangaId from category $categoryId" }
} }
.collect() .collect()
suspend fun await(manga: Manga, category: Category, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, category) suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from category ${category.name}" } log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from category ${category.name}" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, categoryId: Long) = if (categoryId != 0L) { fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.removeMangaFromCategory(mangaId, categoryId) categoryRepository.removeMangaFromCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) } .map { serverListeners.updateCategoryManga(categoryId) }
} else { } else {
@@ -46,7 +58,10 @@ class RemoveMangaFromCategory @Inject constructor(
} }
} }
fun asFlow(manga: Manga, category: Category) = if (category.id != 0L) { fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.removeMangaFromCategory(manga.id, category.id) categoryRepository.removeMangaFromCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) } .map { serverListeners.updateCategoryManga(category.id) }
} else { } else {
@@ -59,4 +74,4 @@ class RemoveMangaFromCategory @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,18 +12,26 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ReorderCategory @Inject constructor(private val categoryRepository: CategoryRepository) { class ReorderCategory
@Inject
suspend fun await(to: Int, from: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(to, from) constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
to: Int,
from: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(to, from)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to move category from $from to $to" } log.warn(it) { "Failed to move category from $from to $to" }
} }
.collect() .collect()
fun asFlow(to: Int, from: Int) = categoryRepository.reorderCategory(to, from) fun asFlow(
to: Int,
from: Int,
) = categoryRepository.reorderCategory(to, from)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,8 +14,9 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateCategoryMeta @Inject constructor(private val categoryRepository: CategoryRepository) { class UpdateCategoryMeta
@Inject
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,
@@ -44,4 +45,4 @@ class UpdateCategoryMeta @Inject constructor(private val categoryRepository: Cat
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -20,11 +20,12 @@ import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
class BatchUpdateChapter @Inject constructor( class BatchUpdateChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
@JvmName("awaitChapters") @JvmName("awaitChapters")
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
@@ -252,4 +253,4 @@ class BatchUpdateChapter @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,42 +16,61 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class DeleteChapterDownload @Inject constructor( class DeleteChapterDownload
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index) mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to delete chapter download for $index of $mangaId" } log.warn(it) { "Failed to delete chapter download for $index of $mangaId" }
} }
.collect() .collect()
suspend fun await(manga: Manga, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index) suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to delete chapter download for $index of ${manga.title}(${manga.id})" } log.warn(it) { "Failed to delete chapter download for $index of ${manga.title}(${manga.id})" }
} }
.collect() .collect()
suspend fun await(chapter: Chapter, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter) suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapter.index} of ${chapter.mangaId}" } log.warn(it) { "Failed to delete chapter download for ${chapter.index} of ${chapter.mangaId}" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, index: Int) = chapterRepository.deleteChapterDownload(mangaId, index) fun asFlow(
mangaId: Long,
index: Int,
) = chapterRepository.deleteChapterDownload(mangaId, index)
.onEach { serverListeners.updateChapters(mangaId, index) } .onEach { serverListeners.updateChapters(mangaId, index) }
fun asFlow(manga: Manga, index: Int) = chapterRepository.deleteChapterDownload(manga.id, index) fun asFlow(
manga: Manga,
index: Int,
) = chapterRepository.deleteChapterDownload(manga.id, index)
.onEach { serverListeners.updateChapters(manga.id, index) } .onEach { serverListeners.updateChapters(manga.id, index) }
fun asFlow(chapter: Chapter) = chapterRepository.deleteChapterDownload(chapter.mangaId, chapter.index) fun asFlow(chapter: Chapter) =
chapterRepository.deleteChapterDownload(chapter.mangaId, chapter.index)
.onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) } .onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,12 +16,17 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetChapter @Inject constructor( class GetChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index) mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -29,7 +34,11 @@ class GetChapter @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index) suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -37,7 +46,10 @@ class GetChapter @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(chapter: Chapter, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter) suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -45,7 +57,10 @@ class GetChapter @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long, index: Int) = serverListeners.combineChapters( fun asFlow(
mangaId: Long,
index: Int,
) = serverListeners.combineChapters(
chapterRepository.getChapter(mangaId, index), chapterRepository.getChapter(mangaId, index),
indexPredate = { id, chapterIndexes -> indexPredate = { id, chapterIndexes ->
id == mangaId && (chapterIndexes == null || index in chapterIndexes) id == mangaId && (chapterIndexes == null || index in chapterIndexes)
@@ -53,7 +68,10 @@ class GetChapter @Inject constructor(
idPredate = { id, _ -> id == mangaId }, idPredate = { id, _ -> id == mangaId },
) )
fun asFlow(manga: Manga, index: Int) = serverListeners.combineChapters( fun asFlow(
manga: Manga,
index: Int,
) = serverListeners.combineChapters(
chapterRepository.getChapter(manga.id, index), chapterRepository.getChapter(manga.id, index),
indexPredate = { id, chapterIndexes -> indexPredate = { id, chapterIndexes ->
id == manga.id && (chapterIndexes == null || index in chapterIndexes) id == manga.id && (chapterIndexes == null || index in chapterIndexes)
@@ -61,7 +79,8 @@ class GetChapter @Inject constructor(
idPredate = { id, _ -> id == manga.id }, idPredate = { id, _ -> id == manga.id },
) )
fun asFlow(chapter: Chapter) = serverListeners.combineChapters( fun asFlow(chapter: Chapter) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapter.mangaId, chapter.index), chapterRepository.getChapter(chapter.mangaId, chapter.index),
indexPredate = { id, chapterIndexes -> indexPredate = { id, chapterIndexes ->
id == chapter.mangaId && (chapterIndexes == null || chapter.index in chapterIndexes) id == chapter.mangaId && (chapterIndexes == null || chapter.index in chapterIndexes)
@@ -72,4 +91,4 @@ class GetChapter @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,8 +15,9 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetChapterPage @Inject constructor(private val chapterRepository: ChapterRepository) { class GetChapterPage
@Inject
constructor(private val chapterRepository: ChapterRepository) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,
@@ -78,4 +79,4 @@ class GetChapterPage @Inject constructor(private val chapterRepository: ChapterR
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,12 +15,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetChapters @Inject constructor( class GetChapters
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -28,7 +32,10 @@ class GetChapters @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -36,13 +43,15 @@ class GetChapters @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineChapters( fun asFlow(mangaId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapters(mangaId), chapterRepository.getChapters(mangaId),
indexPredate = { id, _ -> id == mangaId }, indexPredate = { id, _ -> id == mangaId },
idPredate = { id, _ -> id == mangaId }, idPredate = { id, _ -> id == mangaId },
) )
fun asFlow(manga: Manga) = serverListeners.combineChapters( fun asFlow(manga: Manga) =
serverListeners.combineChapters(
chapterRepository.getChapters(manga.id), chapterRepository.getChapters(manga.id),
indexPredate = { id, _ -> id == manga.id }, indexPredate = { id, _ -> id == manga.id },
idPredate = { id, _ -> id == manga.id }, idPredate = { id, _ -> id == manga.id },
@@ -51,4 +60,4 @@ class GetChapters @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,32 +15,41 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class RefreshChapters @Inject constructor( class RefreshChapters
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to refresh chapters for $mangaId" } log.warn(it) { "Failed to refresh chapters for $mangaId" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to refresh chapters for ${manga.title}(${manga.id})" } log.warn(it) { "Failed to refresh chapters for ${manga.title}(${manga.id})" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = chapterRepository.getChapters(mangaId, true) fun asFlow(mangaId: Long) =
chapterRepository.getChapters(mangaId, true)
.onEach { serverListeners.updateChapters(mangaId) } .onEach { serverListeners.updateChapters(mangaId) }
fun asFlow(manga: Manga) = chapterRepository.getChapters(manga.id, true) fun asFlow(manga: Manga) =
chapterRepository.getChapters(manga.id, true)
.onEach { serverListeners.updateChapters(manga.id) } .onEach { serverListeners.updateChapters(manga.id) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,11 +16,12 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateChapterBookmarked @Inject constructor( class UpdateChapterBookmarked
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,
@@ -88,4 +89,4 @@ class UpdateChapterBookmarked @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,11 +16,12 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateChapterLastPageRead @Inject constructor( class UpdateChapterLastPageRead
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,
@@ -88,4 +89,4 @@ class UpdateChapterLastPageRead @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,11 +16,12 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateChapterMarkPreviousRead @Inject constructor( class UpdateChapterMarkPreviousRead
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,
@@ -71,9 +72,8 @@ class UpdateChapterMarkPreviousRead @Inject constructor(
markPreviousRead = true, markPreviousRead = true,
).onEach { serverListeners.updateChapters(manga.id, index) } ).onEach { serverListeners.updateChapters(manga.id, index) }
fun asFlow( fun asFlow(chapter: Chapter) =
chapter: Chapter, chapterRepository.updateChapter(
) = chapterRepository.updateChapter(
mangaId = chapter.mangaId, mangaId = chapter.mangaId,
chapterIndex = chapter.index, chapterIndex = chapter.index,
markPreviousRead = true, markPreviousRead = true,
@@ -82,4 +82,4 @@ class UpdateChapterMarkPreviousRead @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,11 +15,12 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateChapterMeta @Inject constructor( class UpdateChapterMeta
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
chapter: Chapter, chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset, pageOffset: Int = chapter.meta.juiPageOffset,
@@ -50,4 +51,4 @@ class UpdateChapterMeta @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,11 +16,12 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateChapterRead @Inject constructor( class UpdateChapterRead
@Inject
constructor(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
mangaId: Long, mangaId: Long,
index: Int, index: Int,
@@ -88,4 +89,4 @@ class UpdateChapterRead @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -25,7 +25,6 @@ import io.ktor.client.statement.HttpResponse
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface ChapterRepository { interface ChapterRepository {
@GET("api/v1/manga/{mangaId}/chapters") @GET("api/v1/manga/{mangaId}/chapters")
fun getChapters( fun getChapters(
@Path("mangaId") mangaId: Long, @Path("mangaId") mangaId: Long,

View File

@@ -13,16 +13,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class BatchChapterDownload @Inject constructor(private val downloadRepository: DownloadRepository) { class BatchChapterDownload
@Inject
suspend fun await(chapterIds: List<Long>, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapterIds) constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to queue chapters $chapterIds for a download" } log.warn(it) { "Failed to queue chapters $chapterIds for a download" }
} }
.collect() .collect()
suspend fun await(vararg chapterIds: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(*chapterIds) suspend fun await(
vararg chapterIds: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(*chapterIds)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to queue chapters ${chapterIds.asList()} for a download" } log.warn(it) { "Failed to queue chapters ${chapterIds.asList()} for a download" }
@@ -36,4 +43,4 @@ class BatchChapterDownload @Inject constructor(private val downloadRepository: D
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ClearDownloadQueue @Inject constructor(private val downloadRepository: DownloadRepository) { class ClearDownloadQueue
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to clear download queue" } log.warn(it) { "Failed to clear download queue" }
@@ -26,4 +28,4 @@ class ClearDownloadQueue @Inject constructor(private val downloadRepository: Dow
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,36 +14,54 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class QueueChapterDownload @Inject constructor(private val downloadRepository: DownloadRepository) { class QueueChapterDownload
@Inject
suspend fun await(mangaId: Long, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index) constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(
mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to queue chapter $index of $mangaId for a download" } log.warn(it) { "Failed to queue chapter $index of $mangaId for a download" }
} }
.collect() .collect()
suspend fun await(manga: Manga, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index) suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to queue chapter $index of ${manga.title}(${manga.id}) for a download" } log.warn(it) { "Failed to queue chapter $index of ${manga.title}(${manga.id}) for a download" }
} }
.collect() .collect()
suspend fun await(chapter: Chapter, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter) suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to queue chapter ${chapter.index} of ${chapter.mangaId} for a download" } log.warn(it) { "Failed to queue chapter ${chapter.index} of ${chapter.mangaId} for a download" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, index: Int) = downloadRepository.queueChapterDownload(mangaId, index) fun asFlow(
mangaId: Long,
index: Int,
) = downloadRepository.queueChapterDownload(mangaId, index)
fun asFlow(manga: Manga, index: Int) = downloadRepository.queueChapterDownload(manga.id, index) fun asFlow(
manga: Manga,
index: Int,
) = downloadRepository.queueChapterDownload(manga.id, index)
fun asFlow(chapter: Chapter) = downloadRepository.queueChapterDownload(chapter.mangaId, chapter.index) fun asFlow(chapter: Chapter) = downloadRepository.queueChapterDownload(chapter.mangaId, chapter.index)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,36 +14,62 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class ReorderChapterDownload @Inject constructor(private val downloadRepository: DownloadRepository) { class ReorderChapterDownload
@Inject
suspend fun await(mangaId: Long, index: Int, to: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index, to) constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(
mangaId: Long,
index: Int,
to: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, to)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to reorder chapter download for $index of $mangaId to $to" } log.warn(it) { "Failed to reorder chapter download for $index of $mangaId to $to" }
} }
.collect() .collect()
suspend fun await(manga: Manga, index: Int, to: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index, to) suspend fun await(
manga: Manga,
index: Int,
to: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, to)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to reorder chapter download for $index of ${manga.title}(${manga.id}) to $to" } log.warn(it) { "Failed to reorder chapter download for $index of ${manga.title}(${manga.id}) to $to" }
} }
.collect() .collect()
suspend fun await(chapter: Chapter, to: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter, to) suspend fun await(
chapter: Chapter,
to: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, to)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to reorder chapter download for ${chapter.index} of ${chapter.mangaId} to $to" } log.warn(it) { "Failed to reorder chapter download for ${chapter.index} of ${chapter.mangaId} to $to" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, index: Int, to: Int) = downloadRepository.reorderChapterDownload(mangaId, index, to) fun asFlow(
mangaId: Long,
index: Int,
to: Int,
) = downloadRepository.reorderChapterDownload(mangaId, index, to)
fun asFlow(manga: Manga, index: Int, to: Int) = downloadRepository.reorderChapterDownload(manga.id, index, to) fun asFlow(
manga: Manga,
index: Int,
to: Int,
) = downloadRepository.reorderChapterDownload(manga.id, index, to)
fun asFlow(chapter: Chapter, to: Int) = downloadRepository.reorderChapterDownload(chapter.mangaId, chapter.index, to) fun asFlow(
chapter: Chapter,
to: Int,
) = downloadRepository.reorderChapterDownload(chapter.mangaId, chapter.index, to)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class StartDownloading @Inject constructor(private val downloadRepository: DownloadRepository) { class StartDownloading
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to start downloader" } log.warn(it) { "Failed to start downloader" }
@@ -26,4 +28,4 @@ class StartDownloading @Inject constructor(private val downloadRepository: Downl
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,36 +14,54 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class StopChapterDownload @Inject constructor(private val downloadRepository: DownloadRepository) { class StopChapterDownload
@Inject
suspend fun await(mangaId: Long, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index) constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(
mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to stop chapter download for $index of $mangaId" } log.warn(it) { "Failed to stop chapter download for $index of $mangaId" }
} }
.collect() .collect()
suspend fun await(manga: Manga, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index) suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to stop chapter download for $index of ${manga.title}(${manga.id})" } log.warn(it) { "Failed to stop chapter download for $index of ${manga.title}(${manga.id})" }
} }
.collect() .collect()
suspend fun await(chapter: Chapter, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter) suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to stop chapter download for ${chapter.index} of ${chapter.mangaId}" } log.warn(it) { "Failed to stop chapter download for ${chapter.index} of ${chapter.mangaId}" }
} }
.collect() .collect()
fun asFlow(mangaId: Long, index: Int) = downloadRepository.stopChapterDownload(mangaId, index) fun asFlow(
mangaId: Long,
index: Int,
) = downloadRepository.stopChapterDownload(mangaId, index)
fun asFlow(manga: Manga, index: Int) = downloadRepository.stopChapterDownload(manga.id, index) fun asFlow(
manga: Manga,
index: Int,
) = downloadRepository.stopChapterDownload(manga.id, index)
fun asFlow(chapter: Chapter) = downloadRepository.stopChapterDownload(chapter.mangaId, chapter.index) fun asFlow(chapter: Chapter) = downloadRepository.stopChapterDownload(chapter.mangaId, chapter.index)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class StopDownloading @Inject constructor(private val downloadRepository: DownloadRepository) { class StopDownloading
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val downloadRepository: DownloadRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to stop downloader" } log.warn(it) { "Failed to stop downloader" }
@@ -26,4 +28,4 @@ class StopDownloading @Inject constructor(private val downloadRepository: Downlo
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -19,10 +19,12 @@ import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
class DownloadService @Inject constructor( class DownloadService
@Inject
constructor(
serverPreferences: ServerPreferences, serverPreferences: ServerPreferences,
client: Http, client: Http,
) : WebsocketService(serverPreferences, client) { ) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status> override val _status: MutableStateFlow<Status>
get() = status get() = status
@@ -51,4 +53,4 @@ class DownloadService @Inject constructor(
it.filter { it.mangaId in mangaIds } it.filter { it.mangaId in mangaIds }
} }
} }
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetExtensionList @Inject constructor(private val extensionRepository: ExtensionRepository) { class GetExtensionList
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val extensionRepository: ExtensionRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get extension list" } log.warn(it) { "Failed to get extension list" }
@@ -26,4 +28,4 @@ class GetExtensionList @Inject constructor(private val extensionRepository: Exte
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,9 +13,13 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class InstallExtension @Inject constructor(private val extensionRepository: ExtensionRepository) { class InstallExtension
@Inject
suspend fun await(extension: Extension, onError: suspend (Throwable) -> Unit = {}) = asFlow(extension) constructor(private val extensionRepository: ExtensionRepository) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to install extension ${extension.apkName}" } log.warn(it) { "Failed to install extension ${extension.apkName}" }
@@ -27,4 +31,4 @@ class InstallExtension @Inject constructor(private val extensionRepository: Exte
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,9 +13,13 @@ import me.tatarka.inject.annotations.Inject
import okio.Path import okio.Path
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class InstallExtensionFile @Inject constructor(private val extensionRepository: ExtensionRepository) { class InstallExtensionFile
@Inject
suspend fun await(path: Path, onError: suspend (Throwable) -> Unit = {}) = asFlow(path) constructor(private val extensionRepository: ExtensionRepository) {
suspend fun await(
path: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(path)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to install extension from $path" } log.warn(it) { "Failed to install extension from $path" }
@@ -27,4 +31,4 @@ class InstallExtensionFile @Inject constructor(private val extensionRepository:
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,9 +13,13 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UninstallExtension @Inject constructor(private val extensionRepository: ExtensionRepository) { class UninstallExtension
@Inject
suspend fun await(extension: Extension, onError: suspend (Throwable) -> Unit = {}) = asFlow(extension) constructor(private val extensionRepository: ExtensionRepository) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to uninstall extension ${extension.apkName}" } log.warn(it) { "Failed to uninstall extension ${extension.apkName}" }
@@ -27,4 +31,4 @@ class UninstallExtension @Inject constructor(private val extensionRepository: Ex
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,9 +13,13 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateExtension @Inject constructor(private val extensionRepository: ExtensionRepository) { class UpdateExtension
@Inject
suspend fun await(extension: Extension, onError: suspend (Throwable) -> Unit = {}) = asFlow(extension) constructor(private val extensionRepository: ExtensionRepository) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to update extension ${extension.apkName}" } log.warn(it) { "Failed to update extension ${extension.apkName}" }
@@ -27,4 +31,4 @@ class UpdateExtension @Inject constructor(private val extensionRepository: Exten
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -58,7 +58,8 @@ interface ExtensionRepository {
): Flow<ByteReadChannel> ): Flow<ByteReadChannel>
companion object { companion object {
fun buildExtensionFormData(file: okio.Path) = formData { fun buildExtensionFormData(file: okio.Path) =
formData {
append( append(
"file", "file",
FileSystem.SYSTEM.source(file).buffer().readByteArray(), FileSystem.SYSTEM.source(file).buffer().readByteArray(),

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetGlobalMeta @Inject constructor(private val globalRepository: GlobalRepository) { class GetGlobalMeta
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val globalRepository: GlobalRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get global meta" } log.warn(it) { "Failed to get global meta" }
@@ -26,4 +28,4 @@ class GetGlobalMeta @Inject constructor(private val globalRepository: GlobalRepo
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,8 +14,9 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateGlobalMeta @Inject constructor(private val globalRepository: GlobalRepository) { class UpdateGlobalMeta
@Inject
constructor(private val globalRepository: GlobalRepository) {
suspend fun await( suspend fun await(
globalMeta: GlobalMeta, globalMeta: GlobalMeta,
example: Int = globalMeta.example, example: Int = globalMeta.example,
@@ -43,4 +44,4 @@ class UpdateGlobalMeta @Inject constructor(private val globalRepository: GlobalR
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,32 +15,41 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class AddMangaToLibrary @Inject constructor( class AddMangaToLibrary
@Inject
constructor(
private val libraryRepository: LibraryRepository, private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to add $mangaId to library" } log.warn(it) { "Failed to add $mangaId to library" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to library" } log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to library" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = libraryRepository.addMangaToLibrary(mangaId) fun asFlow(mangaId: Long) =
libraryRepository.addMangaToLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) } .onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = libraryRepository.addMangaToLibrary(manga.id) fun asFlow(manga: Manga) =
libraryRepository.addMangaToLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) } .onEach { serverListeners.updateManga(manga.id) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,32 +15,41 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class RemoveMangaFromLibrary @Inject constructor( class RemoveMangaFromLibrary
@Inject
constructor(
private val libraryRepository: LibraryRepository, private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to remove $mangaId from library" } log.warn(it) { "Failed to remove $mangaId from library" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from library" } log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from library" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = libraryRepository.removeMangaFromLibrary(mangaId) fun asFlow(mangaId: Long) =
libraryRepository.removeMangaFromLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) } .onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = libraryRepository.removeMangaFromLibrary(manga.id) fun asFlow(manga: Manga) =
libraryRepository.removeMangaFromLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) } .onEach { serverListeners.updateManga(manga.id) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -14,7 +14,9 @@ import kotlinx.serialization.Transient
@Serializable @Serializable
@Stable @Stable
enum class DisplayMode(@Transient val res: StringResource) { enum class DisplayMode(
@Transient val res: StringResource,
) {
CompactGrid(MR.strings.display_compact), CompactGrid(MR.strings.display_compact),
ComfortableGrid(MR.strings.display_comfortable), ComfortableGrid(MR.strings.display_comfortable),
CoverOnlyGrid(MR.strings.display_cover_only), CoverOnlyGrid(MR.strings.display_cover_only),

View File

@@ -14,7 +14,9 @@ import kotlinx.serialization.Transient
@Serializable @Serializable
@Stable @Stable
enum class Sort(@Transient val res: StringResource) { enum class Sort(
@Transient val res: StringResource,
) {
ALPHABETICAL(MR.strings.sort_alphabetical), ALPHABETICAL(MR.strings.sort_alphabetical),
// LAST_READ, // LAST_READ,

View File

@@ -12,7 +12,6 @@ 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(private val preferenceStore: PreferenceStore) {
fun showAllCategory(): Preference<Boolean> { fun showAllCategory(): Preference<Boolean> {
return preferenceStore.getBoolean("show_all_category", false) return preferenceStore.getBoolean("show_all_category", false)
} }

View File

@@ -13,7 +13,6 @@ import io.ktor.client.statement.HttpResponse
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface LibraryRepository { interface LibraryRepository {
@GET("api/v1/manga/{mangaId}/library") @GET("api/v1/manga/{mangaId}/library")
fun addMangaToLibrary( fun addMangaToLibrary(
@Path("mangaId") mangaId: Long, @Path("mangaId") mangaId: Long,

View File

@@ -17,11 +17,12 @@ import kotlinx.serialization.decodeFromString
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class LibraryUpdateService @Inject constructor( class LibraryUpdateService
@Inject
constructor(
serverPreferences: ServerPreferences, serverPreferences: ServerPreferences,
client: Http, client: Http,
) : WebsocketService(serverPreferences, client) { ) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status> override val _status: MutableStateFlow<Status>
get() = status get() = status
@@ -38,4 +39,4 @@ class LibraryUpdateService @Inject constructor(
val status = MutableStateFlow(Status.STARTING) val status = MutableStateFlow(Status.STARTING)
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), false)) val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), false))
} }
} }

View File

@@ -15,12 +15,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetManga @Inject constructor( class GetManga
@Inject
constructor(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -28,7 +32,10 @@ class GetManga @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -36,15 +43,17 @@ class GetManga @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineMangaUpdates( fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(mangaId), mangaRepository.getManga(mangaId),
) { mangaId in it } ) { mangaId in it }
fun asFlow(manga: Manga) = serverListeners.combineMangaUpdates( fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(manga.id), mangaRepository.getManga(manga.id),
) { manga.id in it } ) { manga.id in it }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -15,12 +15,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetMangaFull @Inject constructor( class GetMangaFull
@Inject
constructor(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -28,7 +32,10 @@ class GetMangaFull @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -36,15 +43,17 @@ class GetMangaFull @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineMangaUpdates( fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(mangaId), mangaRepository.getMangaFull(mangaId),
) { mangaId in it } ) { mangaId in it }
fun asFlow(manga: Manga) = serverListeners.combineMangaUpdates( fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(manga.id), mangaRepository.getMangaFull(manga.id),
) { manga.id in it } ) { manga.id in it }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,12 +16,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class RefreshManga @Inject constructor( class RefreshManga
@Inject
constructor(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -29,7 +33,10 @@ class RefreshManga @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -37,13 +44,11 @@ class RefreshManga @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = fun asFlow(mangaId: Long) = mangaRepository.getManga(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
mangaRepository.getManga(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = fun asFlow(manga: Manga) = mangaRepository.getManga(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
mangaRepository.getManga(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -16,12 +16,16 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class RefreshMangaFull @Inject constructor( class RefreshMangaFull
@Inject
constructor(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await(
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId) mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -29,7 +33,10 @@ class RefreshMangaFull @Inject constructor(
} }
.singleOrNull() .singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga) suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1) .take(1)
.catch { .catch {
onError(it) onError(it)
@@ -37,13 +44,11 @@ class RefreshMangaFull @Inject constructor(
} }
.singleOrNull() .singleOrNull()
fun asFlow(mangaId: Long) = fun asFlow(mangaId: Long) = mangaRepository.getMangaFull(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
mangaRepository.getMangaFull(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = fun asFlow(manga: Manga) = mangaRepository.getMangaFull(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
mangaRepository.getMangaFull(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -17,11 +17,12 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdateMangaMeta @Inject constructor( class UpdateMangaMeta
@Inject
constructor(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners, private val serverListeners: ServerListeners,
) { ) {
suspend fun await( suspend fun await(
manga: Manga, manga: Manga,
readerMode: String = manga.meta.juiReaderMode, readerMode: String = manga.meta.juiReaderMode,
@@ -51,4 +52,4 @@ class UpdateMangaMeta @Inject constructor(
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -59,7 +59,9 @@ data class MangaMeta(
@Serializable @Serializable
@Stable @Stable
enum class MangaStatus(@Transient val res: StringResource) { enum class MangaStatus(
@Transient val res: StringResource,
) {
UNKNOWN(MR.strings.status_unknown), UNKNOWN(MR.strings.status_unknown),
ONGOING(MR.strings.status_ongoing), ONGOING(MR.strings.status_ongoing),
COMPLETED(MR.strings.status_completed), COMPLETED(MR.strings.status_completed),

View File

@@ -11,11 +11,12 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.domain.reader.service.ReaderPreferences import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
class RunMigrations @Inject constructor( class RunMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences, private val migrationPreferences: MigrationPreferences,
private val readerPreferences: ReaderPreferences, private val readerPreferences: ReaderPreferences,
) { ) {
fun runMigrations() { fun runMigrations() {
val code = migrationPreferences.version().get() val code = migrationPreferences.version().get()
if (code <= 0) { if (code <= 0) {
@@ -26,4 +27,4 @@ class RunMigrations @Inject constructor(
return return
} }
} }
} }

View File

@@ -14,7 +14,9 @@ import kotlinx.serialization.Transient
@Serializable @Serializable
@Stable @Stable
enum class Direction(@Transient val res: StringResource) { enum class Direction(
@Transient val res: StringResource,
) {
Down(MR.strings.dir_down), Down(MR.strings.dir_down),
Left(MR.strings.dir_rtl), Left(MR.strings.dir_rtl),
Right(MR.strings.dir_ltr), Right(MR.strings.dir_ltr),

View File

@@ -14,7 +14,9 @@ import kotlinx.serialization.Transient
@Serializable @Serializable
@Stable @Stable
enum class ImageScale(@Transient val res: StringResource) { enum class ImageScale(
@Transient val res: StringResource,
) {
FitScreen(MR.strings.scale_fit_screen), FitScreen(MR.strings.scale_fit_screen),
Stretch(MR.strings.scale_stretch), Stretch(MR.strings.scale_stretch),
FitWidth(MR.strings.scale_fit_width), FitWidth(MR.strings.scale_fit_width),

View File

@@ -14,7 +14,9 @@ import kotlinx.serialization.Transient
@Serializable @Serializable
@Stable @Stable
enum class NavigationMode(@Transient val res: StringResource) { enum class NavigationMode(
@Transient val res: StringResource,
) {
Disabled(MR.strings.disabled), Disabled(MR.strings.disabled),
LNavigation(MR.strings.nav_l_shaped), LNavigation(MR.strings.nav_l_shaped),
KindlishNavigation(MR.strings.nav_kindle_ish), KindlishNavigation(MR.strings.nav_kindle_ish),

View File

@@ -13,7 +13,6 @@ 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(private val preferenceStore: PreferenceStore, private val factory: (String) -> PreferenceStore) {
fun preload(): Preference<Int> { fun preload(): Preference<Int> {
return preferenceStore.getInt("preload", 3) return preferenceStore.getInt("preload", 3)
} }

View File

@@ -40,7 +40,10 @@ expect val Engine: HttpClientEngineFactory<HttpClientEngineConfig>
expect fun HttpClientConfig<HttpClientEngineConfig>.configurePlatform() expect fun HttpClientConfig<HttpClientEngineConfig>.configurePlatform()
fun httpClient(serverPreferences: ServerPreferences, json: Json): Http { fun httpClient(
serverPreferences: ServerPreferences,
json: Json,
): Http {
return HttpClient(Engine) { return HttpClient(Engine) {
configurePlatform() configurePlatform()

View File

@@ -14,7 +14,6 @@ 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(private val preferenceStore: PreferenceStore) {
fun server(): Preference<String> { fun server(): Preference<String> {
return preferenceStore.getString("server_url", "http://localhost") return preferenceStore.getString("server_url", "http://localhost")
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class AboutServer @Inject constructor(private val settingsRepository: SettingsRepository) { class AboutServer
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val settingsRepository: SettingsRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get server information" } log.warn(it) { "Failed to get server information" }
@@ -26,4 +28,4 @@ class AboutServer @Inject constructor(private val settingsRepository: SettingsRe
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -12,9 +12,11 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class CheckUpdate @Inject constructor(private val settingsRepository: SettingsRepository) { class CheckUpdate
@Inject
suspend fun await(onError: suspend (Throwable) -> Unit = {}) = asFlow() constructor(private val settingsRepository: SettingsRepository) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to check for server updates" } log.warn(it) { "Failed to check for server updates" }
@@ -26,4 +28,4 @@ class CheckUpdate @Inject constructor(private val settingsRepository: SettingsRe
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,27 +13,42 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetFilterList @Inject constructor(private val sourceRepository: SourceRepository) { class GetFilterList
@Inject
suspend fun await(source: Source, reset: Boolean, onError: suspend (Throwable) -> Unit = {}) = asFlow(source.id, reset) constructor(private val sourceRepository: SourceRepository) {
suspend fun await(
source: Source,
reset: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, reset)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get filter list for ${source.displayName} with reset = $reset" } log.warn(it) { "Failed to get filter list for ${source.displayName} with reset = $reset" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(sourceId: Long, reset: Boolean, onError: suspend (Throwable) -> Unit = {}) = asFlow(sourceId, reset) suspend fun await(
sourceId: Long,
reset: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, reset)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get filter list for $sourceId with reset = $reset" } log.warn(it) { "Failed to get filter list for $sourceId with reset = $reset" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(source: Source, reset: Boolean) = sourceRepository.getFilterList(source.id, reset) fun asFlow(
source: Source,
reset: Boolean,
) = sourceRepository.getFilterList(source.id, reset)
fun asFlow(sourceId: Long, reset: Boolean) = sourceRepository.getFilterList(sourceId, reset) fun asFlow(
sourceId: Long,
reset: Boolean,
) = sourceRepository.getFilterList(sourceId, reset)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

View File

@@ -13,27 +13,42 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class GetLatestManga @Inject constructor(private val sourceRepository: SourceRepository) { class GetLatestManga
@Inject
suspend fun await(source: Source, page: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(source.id, page) constructor(private val sourceRepository: SourceRepository) {
suspend fun await(
source: Source,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, page)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get latest manga from ${source.displayName} on page $page" } log.warn(it) { "Failed to get latest manga from ${source.displayName} on page $page" }
} }
.singleOrNull() .singleOrNull()
suspend fun await(sourceId: Long, page: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(sourceId, page) suspend fun await(
sourceId: Long,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, page)
.catch { .catch {
onError(it) onError(it)
log.warn(it) { "Failed to get latest manga from $sourceId on page $page" } log.warn(it) { "Failed to get latest manga from $sourceId on page $page" }
} }
.singleOrNull() .singleOrNull()
fun asFlow(source: Source, page: Int) = sourceRepository.getLatestManga(source.id, page) fun asFlow(
source: Source,
page: Int,
) = sourceRepository.getLatestManga(source.id, page)
fun asFlow(sourceId: Long, page: Int) = sourceRepository.getLatestManga(sourceId, page) fun asFlow(
sourceId: Long,
page: Int,
) = sourceRepository.getLatestManga(sourceId, page)
companion object { companion object {
private val log = logging() private val log = logging()
} }
} }

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