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
class App : Application(), DefaultLifecycleObserver {
override fun onCreate() {
super<Application>.onCreate()

View File

@@ -23,7 +23,6 @@ abstract class AppComponent(
@get:Provides
val context: Context,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
abstract val appMigrations: AppMigrations
@get:AppScope
@@ -39,7 +38,8 @@ abstract class AppComponent(
private var appComponentInstance: AppComponent? = null
@Suppress("UNRESOLVED_REFERENCE", "EXPRESSION_EXPECTED_PACKAGE_FOUND")
fun getInstance(context: Context) = appComponentInstance ?: create(context)
.also { appComponentInstance = it }
fun getInstance(context: Context) =
appComponentInstance ?: create(context)
.also { appComponentInstance = it }
}
}

View File

@@ -11,24 +11,25 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.uicore.vm.ContextWrapper
import me.tatarka.inject.annotations.Inject
class AppMigrations @Inject constructor(
private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper,
) {
class AppMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper,
) {
fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.VERSION_CODE) {
migrationPreferences.appVersion().set(BuildConfig.VERSION_CODE)
fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.VERSION_CODE) {
migrationPreferences.appVersion().set(BuildConfig.VERSION_CODE)
UpdateCheckWorker.setupTask(contextWrapper)
UpdateCheckWorker.setupTask(contextWrapper)
// Fresh install
if (oldVersion == 0) {
return false
// Fresh install
if (oldVersion == 0) {
return false
}
return true
}
return true
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
class ReaderActivity : AppCompatActivity() {
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 {
putExtra("manga", mangaId)
putExtra("chapter", chapterIndex)

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ import ca.gosyer.jui.i18n.MR
import dev.icerock.moko.resources.desc.desc
object Notifications {
/**
* 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.
* @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)
// .setColor(getColor(R.color.accent_blue))
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.
* @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)
return builder.build()
}

View File

@@ -10,15 +10,17 @@ import android.content.Context
import com.russhwolf.settings.SharedPreferencesSettings
import me.tatarka.inject.annotations.Inject
actual class PreferenceStoreFactory @Inject constructor(private val context: Context) {
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
SharedPreferencesSettings(
context.getSharedPreferences(
names.joinToString(separator = "_"),
Context.MODE_PRIVATE,
actual class PreferenceStoreFactory
@Inject
constructor(private val context: Context) {
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
SharedPreferencesSettings(
context.getSharedPreferences(
names.joinToString(separator = "_"),
Context.MODE_PRIVATE,
),
),
),
)
)
}
}
}

View File

@@ -48,17 +48,11 @@ fun CoroutineScope.launchIO(
block: suspend CoroutineScope.() -> Unit,
) = launch(Dispatchers.IO, start, block)
suspend fun <T> withDefaultContext(
block: suspend CoroutineScope.() -> T,
): T = withContext(Dispatchers.Default, block)
suspend fun <T> withDefaultContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Default, block)
suspend fun <T> withUIContext(
block: suspend CoroutineScope.() -> T,
): T = withContext(Dispatchers.Main, block)
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext(
block: suspend CoroutineScope.() -> T,
): T = withContext(Dispatchers.IO, block)
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)
fun Throwable.throwIfCancellation() { if (this is CancellationException) throw this }

View File

@@ -29,7 +29,6 @@ internal open class ProcessChannel<T>(
internal val inChannel: Channel<T>,
internal val outChannel: Channel<T>,
) : Channel<T> {
@DelicateCoroutinesApi
override val isClosedForReceive: Boolean
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.
* 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) {
take(count - replacement.length) + replacement
} else {

View File

@@ -16,46 +16,63 @@ import kotlinx.serialization.modules.SerializersModule
class LazyPreferenceStore(
private val lazyStore: Lazy<PreferenceStore>,
) : PreferenceStore {
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}

View File

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

View File

@@ -15,36 +15,53 @@ import kotlinx.serialization.modules.SerializersModule
* persist these preferences on disk.
*/
interface PreferenceStore {
/**
* 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].
*/
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].
*/
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].
*/
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].
*/
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].
*/
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

View File

@@ -17,81 +17,150 @@ import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
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> {
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.
}
override fun set(key: String, value: String, editor: ObservableSettings) {
override fun set(
key: String,
value: String,
editor: ObservableSettings,
) {
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() }
}
}
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)
}
override fun set(key: String, value: Long, editor: ObservableSettings) {
override fun set(
key: String,
value: Long,
editor: ObservableSettings,
) {
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() }
}
}
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)
}
override fun set(key: String, value: Int, editor: ObservableSettings) {
override fun set(
key: String,
value: Int,
editor: ObservableSettings,
) {
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() }
}
}
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)
}
override fun set(key: String, value: Float, editor: ObservableSettings) {
override fun set(
key: String,
value: Float,
editor: ObservableSettings,
) {
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() }
}
}
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)
}
override fun set(key: String, value: Boolean, editor: ObservableSettings) {
override fun set(
key: String,
value: Boolean,
editor: ObservableSettings,
) {
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() }
}
}
@@ -99,18 +168,28 @@ internal object BooleanAdapter : Adapter<Boolean> {
internal object StringSetAdapter : Adapter<Set<String>> {
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.
}
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)
}
/**
* 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")
}
@@ -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
* 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() }
}
}
@@ -127,16 +210,26 @@ internal class ObjectAdapter<T>(
private val serializer: (T) -> String,
private val deserializer: (String) -> T,
) : Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
override fun get(
key: String,
preferences: ObservableSettings,
): T {
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))
}
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() }
}
}
@@ -146,12 +239,18 @@ internal class JsonObjectAdapter<T>(
private val serializer: KSerializer<T>,
private val serializersModule: SerializersModule = EmptySerializersModule(),
) : Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
override fun get(
key: String,
preferences: ObservableSettings,
): T {
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)
}
@@ -159,14 +258,21 @@ internal class JsonObjectAdapter<T>(
* 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.
*/
override fun isSet(keys: Set<String>, key: String): Boolean {
override fun isSet(
keys: Set<String>,
key: String,
): Boolean {
return keys.any { it.startsWith(key) }
}
/**
* 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
return preferences.addListener(key) { callback() }
}

View File

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

View File

@@ -11,46 +11,63 @@ import kotlinx.serialization.KSerializer
import kotlinx.serialization.modules.SerializersModule
class StandardPreferenceStore(private val preferences: ObservableSettings) : PreferenceStore {
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}
/**
* 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)
}

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].
*/
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()
newList[position] = newItem
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
* [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 ->
if (predicate(element)) {
return replace(index, newItem)

View File

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

View File

@@ -10,15 +10,17 @@ import com.russhwolf.settings.PreferencesSettings
import me.tatarka.inject.annotations.Inject
import java.util.prefs.Preferences
actual class PreferenceStoreFactory @Inject constructor() {
private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui")
actual class PreferenceStoreFactory
@Inject
constructor() {
private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui")
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
PreferencesSettings(
rootNode.node(names.joinToString(separator = "/")),
),
)
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
PreferencesSettings(
rootNode.node(names.joinToString(separator = "/")),
),
)
}
}
}

View File

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

View File

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

View File

@@ -11,10 +11,12 @@ import java.util.Locale as PlatformLocale
fun Locale.toPlatform(): PlatformLocale = PlatformLocale.forLanguageTag(toLanguageTag())
actual fun Locale.getDisplayLanguage(displayLocale: Locale): String = toPlatform()
.getDisplayLanguage(displayLocale.toPlatform())
actual fun Locale.getDisplayLanguage(displayLocale: Locale): String =
toPlatform()
.getDisplayLanguage(displayLocale.toPlatform())
actual fun Locale.getDisplayName(displayLocale: Locale): String = toPlatform()
.getDisplayName(displayLocale.toPlatform())
actual fun Locale.getDisplayName(displayLocale: Locale): String =
toPlatform()
.getDisplayName(displayLocale.toPlatform())
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
interface DataComponent {
@Provides
fun ktorfit(http: Http, serverPreferences: ServerPreferences) = Ktorfit
fun ktorfit(
http: Http,
serverPreferences: ServerPreferences,
) = Ktorfit
.Builder()
.httpClient(http)
.converterFactories(FlowConverterFactory())

View File

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

View File

@@ -15,44 +15,47 @@ import platform.Foundation.NSDateFormatter
import platform.Foundation.NSDateFormatterNoStyle
import platform.Foundation.NSDateFormatterShortStyle
actual class DateHandler @Inject constructor() {
actual val formatOptions by lazy {
listOf(
"",
"MM/dd/yy",
"dd/MM/yy",
"yyyy-MM-dd",
)
}
actual fun getDateFormat(format: String): (Instant) -> String = when (format) {
"" -> NSDateFormatter()
.apply {
setDateStyle(NSDateFormatterShortStyle)
setTimeStyle(NSDateFormatterNoStyle)
setLocale(Locale.current.toPlatform())
}
else -> NSDateFormatter()
.apply {
setDateFormat(format)
}
}.let { formatter ->
{
formatter.stringFromDate(it.toNSDate())
actual class DateHandler
@Inject
constructor() {
actual val formatOptions by lazy {
listOf(
"",
"MM/dd/yy",
"dd/MM/yy",
"yyyy-MM-dd",
)
}
}
actual val dateTimeFormat: (Instant) -> String by lazy {
NSDateFormatter()
.apply {
setDateStyle(NSDateFormatterShortStyle)
setTimeStyle(NSDateFormatterShortStyle)
setLocale(Locale.current.toPlatform())
}
.let { formatter ->
actual fun getDateFormat(format: String): (Instant) -> String =
when (format) {
"" -> NSDateFormatter()
.apply {
setDateStyle(NSDateFormatterShortStyle)
setTimeStyle(NSDateFormatterNoStyle)
setLocale(Locale.current.toPlatform())
}
else -> NSDateFormatter()
.apply {
setDateFormat(format)
}
}.let { formatter ->
{
formatter.stringFromDate(it.toNSDate())
}
}
actual val dateTimeFormat: (Instant) -> String by lazy {
NSDateFormatter()
.apply {
setDateStyle(NSDateFormatterShortStyle)
setTimeStyle(NSDateFormatterShortStyle)
setLocale(Locale.current.toPlatform())
}
.let { formatter ->
{
formatter.stringFromDate(it.toNSDate())
}
}
}
}
}

View File

@@ -15,37 +15,40 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
actual class DateHandler @Inject constructor() {
actual val formatOptions by lazy {
listOf(
"",
"MM/dd/yy",
"dd/MM/yy",
"yyyy-MM-dd",
)
}
actual fun getDateFormat(format: String): (Instant) -> String = when (format) {
"" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.current.toPlatform())
.withZone(ZoneId.systemDefault())
else -> DateTimeFormatter.ofPattern(format)
.withZone(ZoneId.systemDefault())
}.let { formatter ->
{
formatter.format(it.toJavaInstant())
actual class DateHandler
@Inject
constructor() {
actual val formatOptions by lazy {
listOf(
"",
"MM/dd/yy",
"dd/MM/yy",
"yyyy-MM-dd",
)
}
}
actual val dateTimeFormat: (Instant) -> String by lazy {
DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.current.toPlatform())
.withZone(ZoneId.systemDefault())
.let { formatter ->
actual fun getDateFormat(format: String): (Instant) -> String =
when (format) {
"" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.current.toPlatform())
.withZone(ZoneId.systemDefault())
else -> DateTimeFormatter.ofPattern(format)
.withZone(ZoneId.systemDefault())
}.let { formatter ->
{
formatter.format(it.toJavaInstant())
}
}
actual val dateTimeFormat: (Instant) -> String by lazy {
DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.current.toPlatform())
.withZone(ZoneId.systemDefault())
.let { formatter ->
{
formatter.format(it.toJavaInstant())
}
}
}
}
}

View File

@@ -21,7 +21,6 @@ abstract class AppComponent(
@get:Provides
val context: ContextWrapper,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
abstract val appMigrations: AppMigrations
@get:AppScope
@@ -35,7 +34,8 @@ abstract class AppComponent(
companion object {
private var appComponentInstance: AppComponent? = null
fun getInstance(context: ContextWrapper) = appComponentInstance ?: create(context)
.also { appComponentInstance = it }
fun getInstance(context: ContextWrapper) =
appComponentInstance ?: create(context)
.also { appComponentInstance = it }
}
}

View File

@@ -11,22 +11,23 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.uicore.vm.ContextWrapper
import me.tatarka.inject.annotations.Inject
class AppMigrations @Inject constructor(
private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper,
) {
class AppMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences,
private val contextWrapper: ContextWrapper,
) {
fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.MIGRATION_CODE) {
migrationPreferences.appVersion().set(BuildConfig.MIGRATION_CODE)
fun runMigrations(): Boolean {
val oldVersion = migrationPreferences.appVersion().get()
if (oldVersion < BuildConfig.MIGRATION_CODE) {
migrationPreferences.appVersion().set(BuildConfig.MIGRATION_CODE)
// Fresh install
if (oldVersion == 0) {
return false
// Fresh install
if (oldVersion == 0) {
return false
}
return true
}
return true
return false
}
return false
}
}

View File

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

View File

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

View File

@@ -23,36 +23,40 @@ import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ServerListeners @Inject constructor() {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
class ServerListeners
@Inject
constructor() {
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) }
private val _mangaListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaListener = _mangaListener.asSharedFlow()
private val _mangaListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaListener = _mangaListener.asSharedFlow()
private val _chapterIndexesListener = MutableSharedFlow<Pair<Long, List<Int>?>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIndexesListener = _chapterIndexesListener.asSharedFlow()
private val _chapterIndexesListener = MutableSharedFlow<Pair<Long, List<Int>?>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIndexesListener = _chapterIndexesListener.asSharedFlow()
private val _chapterIdsListener = MutableSharedFlow<Pair<Long?, List<Long>>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
private val _chapterIdsListener = MutableSharedFlow<Pair<Long?, List<Long>>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
private val categoryMangaListener = MutableSharedFlow<Long>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val categoryMangaListener = MutableSharedFlow<Long>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val extensionListener = MutableSharedFlow<List<String>>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val extensionListener = MutableSharedFlow<List<String>>(
extraBufferCapacity = Channel.UNLIMITED,
)
fun <T> combineMangaUpdates(flow: Flow<T>, predate: (suspend (List<Long>) -> Boolean)? = null) =
if (predate != null) {
fun <T> combineMangaUpdates(
flow: Flow<T>,
predate: (suspend (List<Long>) -> Boolean)? = null,
) = if (predate != null) {
_mangaListener
.filter(predate)
.startWith(Unit)
@@ -62,14 +66,16 @@ class ServerListeners @Inject constructor() {
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun updateManga(vararg ids: Long) {
scope.launch {
_mangaListener.emit(ids.toList())
fun updateManga(vararg ids: Long) {
scope.launch {
_mangaListener.emit(ids.toList())
}
}
}
fun <T> combineCategoryManga(flow: Flow<T>, predate: (suspend (Long) -> Boolean)? = null) =
if (predate != null) {
fun <T> combineCategoryManga(
flow: Flow<T>,
predate: (suspend (Long) -> Boolean)? = null,
) = if (predate != null) {
categoryMangaListener.filter(predate).startWith(-1)
} else {
categoryMangaListener.startWith(-1)
@@ -77,58 +83,70 @@ class ServerListeners @Inject constructor() {
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun updateCategoryManga(id: Long) {
scope.launch {
categoryMangaListener.emit(id)
}
}
fun <T> combineChapters(
flow: Flow<T>,
indexPredate: (suspend (Long, List<Int>?) -> Boolean)? = null,
idPredate: (suspend (Long?, List<Long>) -> Boolean)? = null,
): Flow<T> {
val indexListener = if (indexPredate != null) {
_chapterIndexesListener.filter { indexPredate(it.first, it.second) }.startWith(Unit)
} else {
_chapterIndexesListener.startWith(Unit)
}
val idsListener = if (idPredate != null) {
_chapterIdsListener.filter { idPredate(it.first, it.second) }.startWith(Unit)
} else {
_chapterIdsListener.startWith(Unit)
fun updateCategoryManga(id: Long) {
scope.launch {
categoryMangaListener.emit(id)
}
}
return combine(indexListener, idsListener) { _, _ -> }
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
}
fun <T> combineChapters(
flow: Flow<T>,
indexPredate: (suspend (Long, List<Int>?) -> Boolean)? = null,
idPredate: (suspend (Long?, List<Long>) -> Boolean)? = null,
): Flow<T> {
val indexListener = if (indexPredate != null) {
_chapterIndexesListener.filter { indexPredate(it.first, it.second) }.startWith(Unit)
} else {
_chapterIndexesListener.startWith(Unit)
}
val idsListener = if (idPredate != null) {
_chapterIdsListener.filter { idPredate(it.first, it.second) }.startWith(Unit)
} else {
_chapterIdsListener.startWith(Unit)
}
fun updateChapters(mangaId: Long, chapterIndexes: List<Int>) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.ifEmpty { null })
return combine(indexListener, idsListener) { _, _ -> }
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
}
fun updateChapters(
mangaId: Long,
chapterIndexes: List<Int>,
) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.ifEmpty { null })
}
}
fun updateChapters(
mangaId: Long,
vararg chapterIndexes: Int,
) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.toList().ifEmpty { null })
}
}
fun updateChapters(
mangaId: Long?,
chapterIds: List<Long>,
) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds)
}
}
fun updateChapters(
mangaId: Long?,
vararg chapterIds: Long,
) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds.toList())
}
}
companion object {
private val log = logging()
}
}
fun updateChapters(mangaId: Long, vararg chapterIndexes: Int) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.toList().ifEmpty { null })
}
}
fun updateChapters(mangaId: Long?, chapterIds: List<Long>) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds)
}
}
fun updateChapters(mangaId: Long?, vararg chapterIds: Long) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds.toList())
}
}
companion object {
private val log = logging()
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,6 @@ import okio.Path
import okio.buffer
interface BackupRepository {
@Multipart
@POST("api/v1/backup/import/file")
fun importBackupFile(
@@ -47,15 +46,16 @@ interface BackupRepository {
): Flow<HttpResponse>
companion object {
fun buildBackupFormData(file: Path) = formData {
append(
"backup.proto.gz",
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
},
)
}
fun buildBackupFormData(file: Path) =
formData {
append(
"backup.proto.gz",
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
},
)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,36 +15,45 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaListFromCategory @Inject constructor(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
class GetMangaListFromCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category $categoryId" }
}
.singleOrNull()
suspend fun await(categoryId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(categoryId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category $categoryId" }
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category ${category.name}" }
}
.singleOrNull()
fun asFlow(categoryId: Long) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(categoryId),
) { categoryId == it }
fun asFlow(category: Category) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(category.id),
) { category.id == it }
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(category: Category, onError: suspend (Throwable) -> Unit = {}) = asFlow(category)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category ${category.name}" }
}
.singleOrNull()
fun asFlow(categoryId: Long) = serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(categoryId),
) { categoryId == it }
fun asFlow(category: Category) = serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(category.id),
) { category.id == it }
companion object {
private val log = logging()
}
}

View File

@@ -13,35 +13,50 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ModifyCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
class ModifyCategory
@Inject
constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
categoryId: Long,
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
categoryId = categoryId,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category $categoryId with options: name=$name" }
}.collect()
suspend fun await(categoryId: Long, name: String, onError: suspend (Throwable) -> Unit = {}) = asFlow(
categoryId = categoryId,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category $categoryId with options: name=$name" }
}.collect()
suspend fun await(
category: Category,
name: String? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
category = category,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" }
}.collect()
suspend fun await(category: Category, name: String? = null, onError: suspend (Throwable) -> Unit = {}) = asFlow(
category = category,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" }
}.collect()
fun asFlow(
categoryId: Long,
name: String,
) = categoryRepository.modifyCategory(
categoryId = categoryId,
name = name,
)
fun asFlow(categoryId: Long, name: String) = categoryRepository.modifyCategory(
categoryId = categoryId,
name = name,
)
fun asFlow(
category: Category,
name: String? = null,
) = categoryRepository.modifyCategory(
categoryId = category.id,
name = name ?: category.name,
)
fun asFlow(category: Category, name: String? = null) = categoryRepository.modifyCategory(
categoryId = category.id,
name = name ?: category.name,
)
companion object {
private val log = logging()
companion object {
private val log = logging()
}
}
}

View File

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

View File

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

View File

@@ -14,34 +14,35 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateCategoryMeta @Inject constructor(private val categoryRepository: CategoryRepository) {
class UpdateCategoryMeta
@Inject
constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(
category: Category,
example: Int = category.meta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category, example)
.catch {
onError(it)
log.warn(it) { "Failed to update ${category.name}(${category.id}) meta" }
}
.collect()
suspend fun await(
category: Category,
example: Int = category.meta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category, example)
.catch {
onError(it)
log.warn(it) { "Failed to update ${category.name}(${category.id}) meta" }
fun asFlow(
category: Category,
example: Int = category.meta.example,
) = flow {
if (example != category.meta.example) {
categoryRepository.updateCategoryMeta(
category.id,
"example",
example.toString(),
).collect()
}
emit(Unit)
}
.collect()
fun asFlow(
category: Category,
example: Int = category.meta.example,
) = flow {
if (example != category.meta.example) {
categoryRepository.updateCategoryMeta(
category.id,
"example",
example.toString(),
).collect()
companion object {
private val log = logging()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -20,236 +20,237 @@ import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class BatchUpdateChapter @Inject constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
class BatchUpdateChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
@JvmName("awaitChapters")
suspend fun await(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
suspend fun await(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
suspend fun await(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
suspend fun await(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
suspend fun await(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update update multiple chapters" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update update multiple chapters" }
}
.collect()
@JvmName("asFlowChapters")
fun asFlow(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
private fun getFlow(
mangaId: Long?,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = if (mangaId != null) {
chapterRepository.batchUpdateChapter(
mangaId,
MangaChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
@JvmName("asFlowChapters")
fun asFlow(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
} else {
chapterRepository.batchUpdateChapter(
ChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
)
}.onEach {
serverListeners.updateChapters(mangaId, chapterIds)
}
companion object {
private val log = logging()
fun asFlow(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
private fun getFlow(
mangaId: Long?,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = if (mangaId != null) {
chapterRepository.batchUpdateChapter(
mangaId,
MangaChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
)
} else {
chapterRepository.batchUpdateChapter(
ChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
)
}.onEach {
serverListeners.updateChapters(mangaId, chapterIds)
}
companion object {
private val log = logging()
}
}
}

View File

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

View File

@@ -16,60 +16,79 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapter @Inject constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
class GetChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId, index)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for $mangaId" }
suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for ${manga.title}(${manga.id})" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(
mangaId: Long,
index: Int,
) = serverListeners.combineChapters(
chapterRepository.getChapter(mangaId, index),
indexPredate = { id, chapterIndexes ->
id == mangaId && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == mangaId },
)
fun asFlow(
manga: Manga,
index: Int,
) = serverListeners.combineChapters(
chapterRepository.getChapter(manga.id, index),
indexPredate = { id, chapterIndexes ->
id == manga.id && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == manga.id },
)
fun asFlow(chapter: Chapter) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapter.mangaId, chapter.index),
indexPredate = { id, chapterIndexes ->
id == chapter.mangaId && (chapterIndexes == null || chapter.index in chapterIndexes)
},
idPredate = { id, _ -> id == chapter.mangaId },
)
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, index: Int, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga, index)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for ${manga.title}(${manga.id})" }
}
.singleOrNull()
suspend fun await(chapter: Chapter, onError: suspend (Throwable) -> Unit = {}) = asFlow(chapter)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(mangaId: Long, index: Int) = serverListeners.combineChapters(
chapterRepository.getChapter(mangaId, index),
indexPredate = { id, chapterIndexes ->
id == mangaId && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == mangaId },
)
fun asFlow(manga: Manga, index: Int) = serverListeners.combineChapters(
chapterRepository.getChapter(manga.id, index),
indexPredate = { id, chapterIndexes ->
id == manga.id && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == manga.id },
)
fun asFlow(chapter: Chapter) = serverListeners.combineChapters(
chapterRepository.getChapter(chapter.mangaId, chapter.index),
indexPredate = { id, chapterIndexes ->
id == chapter.mangaId && (chapterIndexes == null || chapter.index in chapterIndexes)
},
idPredate = { id, _ -> id == chapter.mangaId },
)
companion object {
private val log = logging()
}
}

View File

@@ -15,67 +15,68 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapterPage @Inject constructor(private val chapterRepository: ChapterRepository) {
class GetChapterPage
@Inject
constructor(private val chapterRepository: ChapterRepository) {
suspend fun await(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for $mangaId" }
}
.singleOrNull()
suspend fun await(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for $mangaId" }
suspend fun await(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for ${manga.title}(${manga.id})" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(mangaId, index, pageNum, block)
fun asFlow(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(manga.id, index, pageNum, block)
fun asFlow(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(chapter.mangaId, chapter.index, pageNum, block)
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for ${manga.title}(${manga.id})" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(mangaId, index, pageNum, block)
fun asFlow(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(manga.id, index, pageNum, block)
fun asFlow(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(chapter.mangaId, chapter.index, pageNum, block)
companion object {
private val log = logging()
}
}

View File

@@ -15,40 +15,49 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapters @Inject constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
class GetChapters
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for $mangaId" }
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapters(mangaId),
indexPredate = { id, _ -> id == mangaId },
idPredate = { id, _ -> id == mangaId },
)
fun asFlow(manga: Manga) =
serverListeners.combineChapters(
chapterRepository.getChapters(manga.id),
indexPredate = { id, _ -> id == manga.id },
idPredate = { id, _ -> id == manga.id },
)
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineChapters(
chapterRepository.getChapters(mangaId),
indexPredate = { id, _ -> id == mangaId },
idPredate = { id, _ -> id == mangaId },
)
fun asFlow(manga: Manga) = serverListeners.combineChapters(
chapterRepository.getChapters(manga.id),
indexPredate = { id, _ -> id == manga.id },
idPredate = { id, _ -> id == manga.id },
)
companion object {
private val log = logging()
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,39 +15,40 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterMeta @Inject constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
class UpdateChapterMeta
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageOffset)
.catch {
onError(it)
log.warn(it) { "Failed to update ${chapter.name}(${chapter.index}) meta" }
}
.collect()
suspend fun await(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageOffset)
.catch {
onError(it)
log.warn(it) { "Failed to update ${chapter.name}(${chapter.index}) meta" }
fun asFlow(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
chapterRepository.updateChapterMeta(
chapter.mangaId,
chapter.index,
"juiPageOffset",
pageOffset.toString(),
).collect()
serverListeners.updateChapters(chapter.mangaId, chapter.index)
}
emit(Unit)
}
.collect()
fun asFlow(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
chapterRepository.updateChapterMeta(
chapter.mangaId,
chapter.index,
"juiPageOffset",
pageOffset.toString(),
).collect()
serverListeners.updateChapters(chapter.mangaId, chapter.index)
companion object {
private val log = logging()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,36 +19,38 @@ import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import me.tatarka.inject.annotations.Inject
class DownloadService @Inject constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
class DownloadService
@Inject
constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
override val query: String
get() = "/api/v1/downloads"
override val query: String
get() = "/api/v1/downloads"
override suspend fun onReceived(frame: Frame.Text) {
val status = json.decodeFromString<DownloadStatus>(frame.readText())
downloaderStatus.value = status.status
downloadQueue.value = status.queue
override suspend fun onReceived(frame: Frame.Text) {
val status = json.decodeFromString<DownloadStatus>(frame.readText())
downloaderStatus.value = status.status
downloadQueue.value = status.queue
}
companion object {
val status = MutableStateFlow(Status.STARTING)
val downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
val downloaderStatus = MutableStateFlow(DownloaderStatus.Stopped)
fun registerWatch(mangaId: Long) =
downloadQueue
.map {
it.filter { it.mangaId == mangaId }
}
fun registerWatches(mangaIds: Set<Long>) =
downloadQueue
.map {
it.filter { it.mangaId in mangaIds }
}
}
}
companion object {
val status = MutableStateFlow(Status.STARTING)
val downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
val downloaderStatus = MutableStateFlow(DownloaderStatus.Stopped)
fun registerWatch(mangaId: Long) =
downloadQueue
.map {
it.filter { it.mangaId == mangaId }
}
fun registerWatches(mangaIds: Set<Long>) =
downloadQueue
.map {
it.filter { it.mangaId in mangaIds }
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -58,15 +58,16 @@ interface ExtensionRepository {
): Flow<ByteReadChannel>
companion object {
fun buildExtensionFormData(file: okio.Path) = formData {
append(
"file",
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=file")
},
)
}
fun buildExtensionFormData(file: okio.Path) =
formData {
append(
"file",
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
Headers.build {
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
append(HttpHeaders.ContentDisposition, "filename=file")
},
)
}
}
}

View File

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

View File

@@ -14,33 +14,34 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateGlobalMeta @Inject constructor(private val globalRepository: GlobalRepository) {
class UpdateGlobalMeta
@Inject
constructor(private val globalRepository: GlobalRepository) {
suspend fun await(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(globalMeta, example)
.catch {
onError(it)
log.warn(it) { "Failed to update global meta" }
}
.collect()
suspend fun await(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(globalMeta, example)
.catch {
onError(it)
log.warn(it) { "Failed to update global meta" }
fun asFlow(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
) = flow {
if (example != globalMeta.example) {
globalRepository.updateGlobalMeta(
"example",
example.toString(),
).collect()
}
emit(Unit)
}
.collect()
fun asFlow(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
) = flow {
if (example != globalMeta.example) {
globalRepository.updateGlobalMeta(
"example",
example.toString(),
).collect()
companion object {
private val log = logging()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,6 @@ import ca.gosyer.jui.domain.library.model.FilterState
import ca.gosyer.jui.domain.library.model.Sort
class LibraryPreferences(private val preferenceStore: PreferenceStore) {
fun showAllCategory(): Preference<Boolean> {
return preferenceStore.getBoolean("show_all_category", false)
}

View File

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

View File

@@ -17,25 +17,26 @@ import kotlinx.serialization.decodeFromString
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class LibraryUpdateService @Inject constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
class LibraryUpdateService
@Inject
constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
override val _status: MutableStateFlow<Status>
get() = status
override val query: String
get() = "/api/v1/update"
override val query: String
get() = "/api/v1/update"
override suspend fun onReceived(frame: Frame.Text) {
updateStatus.value = json.decodeFromString<UpdateStatus>(frame.readText())
}
override suspend fun onReceived(frame: Frame.Text) {
updateStatus.value = json.decodeFromString<UpdateStatus>(frame.readText())
companion object {
private val log = logging()
val status = MutableStateFlow(Status.STARTING)
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), false))
}
}
companion object {
private val log = logging()
val status = MutableStateFlow(Status.STARTING)
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), false))
}
}

View File

@@ -15,36 +15,45 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetManga @Inject constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
class GetManga
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga $mangaId" }
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineMangaUpdates(
mangaRepository.getManga(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) = serverListeners.combineMangaUpdates(
mangaRepository.getManga(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
}
}

View File

@@ -15,36 +15,45 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaFull @Inject constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
class GetMangaFull
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga $mangaId" }
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) = serverListeners.combineMangaUpdates(
mangaRepository.getMangaFull(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
}
}

View File

@@ -16,34 +16,39 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RefreshManga @Inject constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
class RefreshManga
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga $mangaId" }
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.getManga(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = mangaRepository.getManga(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
mangaRepository.getManga(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
mangaRepository.getManga(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -16,34 +16,39 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RefreshMangaFull @Inject constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
class RefreshMangaFull
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga $mangaId" }
}
.singleOrNull()
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga $mangaId" }
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.getMangaFull(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = mangaRepository.getMangaFull(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
mangaRepository.getMangaFull(mangaId, true).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
mangaRepository.getMangaFull(manga.id, true).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -17,38 +17,39 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateMangaMeta @Inject constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
class UpdateMangaMeta
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, readerMode)
.catch {
onError(it)
log.warn(it) { "Failed to update ${manga.title}(${manga.id}) meta" }
}
.collect()
suspend fun await(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, readerMode)
.catch {
onError(it)
log.warn(it) { "Failed to update ${manga.title}(${manga.id}) meta" }
fun asFlow(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode.decodeURLQueryComponent(),
) = flow {
if (readerMode.encodeURLQueryComponent() != manga.meta.juiReaderMode) {
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode,
).collect()
serverListeners.updateManga(manga.id)
}
emit(Unit)
}
.collect()
fun asFlow(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode.decodeURLQueryComponent(),
) = flow {
if (readerMode.encodeURLQueryComponent() != manga.meta.juiReaderMode) {
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode,
).collect()
serverListeners.updateManga(manga.id)
companion object {
private val log = logging()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

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

View File

@@ -11,19 +11,20 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import me.tatarka.inject.annotations.Inject
class RunMigrations @Inject constructor(
private val migrationPreferences: MigrationPreferences,
private val readerPreferences: ReaderPreferences,
) {
fun runMigrations() {
val code = migrationPreferences.version().get()
if (code <= 0) {
readerPreferences.modes().get().forEach {
readerPreferences.getMode(it).direction().delete()
class RunMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences,
private val readerPreferences: ReaderPreferences,
) {
fun runMigrations() {
val code = migrationPreferences.version().get()
if (code <= 0) {
readerPreferences.modes().get().forEach {
readerPreferences.getMode(it).direction().delete()
}
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
return
}
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
return
}
}
}

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,6 @@ import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
class ReaderPreferences(private val preferenceStore: PreferenceStore, private val factory: (String) -> PreferenceStore) {
fun preload(): Preference<Int> {
return preferenceStore.getInt("preload", 3)
}

View File

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

View File

@@ -14,7 +14,6 @@ import ca.gosyer.jui.domain.server.model.ServerUrlPreference
import io.ktor.http.Url
class ServerPreferences(private val preferenceStore: PreferenceStore) {
fun server(): Preference<String> {
return preferenceStore.getString("server_url", "http://localhost")
}

View File

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

View File

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

View File

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

View File

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

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