mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Minor cleanup
This commit is contained in:
		@@ -72,9 +72,9 @@ class SetReadStatus(
 | 
			
		||||
    suspend fun await(manga: Manga, read: Boolean) =
 | 
			
		||||
        await(manga.id, read)
 | 
			
		||||
 | 
			
		||||
    sealed class Result {
 | 
			
		||||
        data object Success : Result()
 | 
			
		||||
        data object NoChapters : Result()
 | 
			
		||||
        data class InternalError(val error: Throwable) : Result()
 | 
			
		||||
    sealed interface Result {
 | 
			
		||||
        data object Success : Result
 | 
			
		||||
        data object NoChapters : Result
 | 
			
		||||
        data class InternalError(val error: Throwable) : Result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.extension.model.Extension
 | 
			
		||||
import eu.kanade.tachiyomi.source.ConfigurableSource
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
 | 
			
		||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
 | 
			
		||||
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
 | 
			
		||||
@@ -65,7 +65,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
 | 
			
		||||
@Composable
 | 
			
		||||
fun ExtensionDetailsScreen(
 | 
			
		||||
    navigateUp: () -> Unit,
 | 
			
		||||
    state: ExtensionDetailsState,
 | 
			
		||||
    state: ExtensionDetailsScreenModel.State,
 | 
			
		||||
    onClickSourcePreferences: (sourceId: Long) -> Unit,
 | 
			
		||||
    onClickWhatsNew: () -> Unit,
 | 
			
		||||
    onClickReadme: () -> Unit,
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.extension.model.Extension
 | 
			
		||||
import eu.kanade.tachiyomi.extension.model.InstallStep
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
 | 
			
		||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
 | 
			
		||||
import tachiyomi.presentation.core.components.material.PullRefresh
 | 
			
		||||
@@ -57,7 +57,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun ExtensionScreen(
 | 
			
		||||
    state: ExtensionsState,
 | 
			
		||||
    state: ExtensionsScreenModel.State,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    searchQuery: String?,
 | 
			
		||||
    onLongClickItem: (Extension) -> Unit,
 | 
			
		||||
@@ -108,7 +108,7 @@ fun ExtensionScreen(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun ExtensionContent(
 | 
			
		||||
    state: ExtensionsState,
 | 
			
		||||
    state: ExtensionsScreenModel.State,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    onLongClickItem: (Extension) -> Unit,
 | 
			
		||||
    onClickItemCancel: (Extension) -> Unit,
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
 | 
			
		||||
import eu.kanade.presentation.components.AppBar
 | 
			
		||||
import eu.kanade.presentation.manga.components.BaseMangaListItem
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
 | 
			
		||||
import tachiyomi.presentation.core.components.material.Scaffold
 | 
			
		||||
@@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
 | 
			
		||||
fun MigrateMangaScreen(
 | 
			
		||||
    navigateUp: () -> Unit,
 | 
			
		||||
    title: String?,
 | 
			
		||||
    state: MigrateMangaState,
 | 
			
		||||
    state: MigrateMangaScreenModel.State,
 | 
			
		||||
    onClickItem: (Manga) -> Unit,
 | 
			
		||||
    onClickCover: (Manga) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
@@ -51,7 +51,7 @@ fun MigrateMangaScreen(
 | 
			
		||||
@Composable
 | 
			
		||||
private fun MigrateMangaContent(
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    state: MigrateMangaState,
 | 
			
		||||
    state: MigrateMangaScreenModel.State,
 | 
			
		||||
    onClickItem: (Manga) -> Unit,
 | 
			
		||||
    onClickCover: (Manga) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
 | 
			
		||||
import eu.kanade.presentation.browse.components.BaseSourceItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.SourceIcon
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
 | 
			
		||||
import tachiyomi.domain.source.model.Source
 | 
			
		||||
import tachiyomi.presentation.core.components.Badge
 | 
			
		||||
@@ -43,7 +43,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun MigrateSourceScreen(
 | 
			
		||||
    state: MigrateSourceState,
 | 
			
		||||
    state: MigrateSourceScreenModel.State,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    onClickItem: (Source) -> Unit,
 | 
			
		||||
    onToggleSortingDirection: () -> Unit,
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import eu.kanade.presentation.browse.components.BaseSourceItem
 | 
			
		||||
import eu.kanade.presentation.components.AppBar
 | 
			
		||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
 | 
			
		||||
import tachiyomi.domain.source.model.Source
 | 
			
		||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
 | 
			
		||||
@@ -22,7 +22,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
 | 
			
		||||
@Composable
 | 
			
		||||
fun SourcesFilterScreen(
 | 
			
		||||
    navigateUp: () -> Unit,
 | 
			
		||||
    state: SourcesFilterState.Success,
 | 
			
		||||
    state: SourcesFilterScreenModel.State.Success,
 | 
			
		||||
    onClickLanguage: (String) -> Unit,
 | 
			
		||||
    onClickSource: (Source) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
@@ -54,7 +54,7 @@ fun SourcesFilterScreen(
 | 
			
		||||
@Composable
 | 
			
		||||
private fun SourcesFilterContent(
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    state: SourcesFilterState.Success,
 | 
			
		||||
    state: SourcesFilterScreenModel.State.Success,
 | 
			
		||||
    onClickLanguage: (String) -> Unit,
 | 
			
		||||
    onClickSource: (Source) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -192,7 +192,7 @@ fun SourceOptionsDialog(
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class SourceUiModel {
 | 
			
		||||
    data class Item(val source: Source) : SourceUiModel()
 | 
			
		||||
    data class Header(val language: String) : SourceUiModel()
 | 
			
		||||
sealed interface SourceUiModel {
 | 
			
		||||
    data class Item(val source: Source) : SourceUiModel
 | 
			
		||||
    data class Header(val language: String) : SourceUiModel
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ import eu.kanade.presentation.components.SearchToolbar
 | 
			
		||||
import eu.kanade.presentation.history.components.HistoryItem
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.ui.history.HistoryState
 | 
			
		||||
import tachiyomi.domain.history.model.HistoryWithRelations
 | 
			
		||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
 | 
			
		||||
import tachiyomi.presentation.core.components.material.Scaffold
 | 
			
		||||
@@ -33,7 +32,7 @@ import java.util.Date
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun HistoryScreen(
 | 
			
		||||
    state: HistoryState,
 | 
			
		||||
    state: HistoryScreenModel.State,
 | 
			
		||||
    snackbarHostState: SnackbarHostState,
 | 
			
		||||
    onSearchQueryChange: (String?) -> Unit,
 | 
			
		||||
    onClickCover: (mangaId: Long) -> Unit,
 | 
			
		||||
@@ -139,7 +138,7 @@ private fun HistoryScreenContent(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class HistoryUiModel {
 | 
			
		||||
    data class Header(val date: Date) : HistoryUiModel()
 | 
			
		||||
    data class Item(val item: HistoryWithRelations) : HistoryUiModel()
 | 
			
		||||
sealed interface HistoryUiModel {
 | 
			
		||||
    data class Header(val date: Date) : HistoryUiModel
 | 
			
		||||
    data class Item(val item: HistoryWithRelations) : HistoryUiModel
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.source.getNameForMangaInfo
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.ChapterItem
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
 | 
			
		||||
import tachiyomi.domain.chapter.model.Chapter
 | 
			
		||||
@@ -82,7 +82,7 @@ import java.util.Date
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun MangaScreen(
 | 
			
		||||
    state: MangaScreenState.Success,
 | 
			
		||||
    state: MangaScreenModel.State.Success,
 | 
			
		||||
    snackbarHostState: SnackbarHostState,
 | 
			
		||||
    dateRelativeTime: Int,
 | 
			
		||||
    dateFormat: DateFormat,
 | 
			
		||||
@@ -210,7 +210,7 @@ fun MangaScreen(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun MangaScreenSmallImpl(
 | 
			
		||||
    state: MangaScreenState.Success,
 | 
			
		||||
    state: MangaScreenModel.State.Success,
 | 
			
		||||
    snackbarHostState: SnackbarHostState,
 | 
			
		||||
    dateRelativeTime: Int,
 | 
			
		||||
    dateFormat: DateFormat,
 | 
			
		||||
@@ -436,7 +436,7 @@ private fun MangaScreenSmallImpl(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun MangaScreenLargeImpl(
 | 
			
		||||
    state: MangaScreenState.Success,
 | 
			
		||||
    state: MangaScreenModel.State.Success,
 | 
			
		||||
    snackbarHostState: SnackbarHostState,
 | 
			
		||||
    dateRelativeTime: Int,
 | 
			
		||||
    dateFormat: DateFormat,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TextButton
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import androidx.compose.runtime.collectAsState
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.rememberCoroutineScope
 | 
			
		||||
@@ -269,12 +270,15 @@ private class ClearDatabaseScreenModel : StateScreenModel<ClearDatabaseScreenMod
 | 
			
		||||
        state.copy(showConfirmation = false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class State {
 | 
			
		||||
        data object Loading : State()
 | 
			
		||||
    sealed interface State {
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data object Loading : State
 | 
			
		||||
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data class Ready(
 | 
			
		||||
            val items: List<SourceWithCount>,
 | 
			
		||||
            val selection: List<Long> = emptyList(),
 | 
			
		||||
            val showConfirmation: Boolean = false,
 | 
			
		||||
        ) : State()
 | 
			
		||||
        ) : State
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@ package eu.kanade.presentation.more.stats
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import eu.kanade.presentation.more.stats.data.StatsData
 | 
			
		||||
 | 
			
		||||
sealed class StatsScreenState {
 | 
			
		||||
sealed interface StatsScreenState {
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data object Loading : StatsScreenState()
 | 
			
		||||
    data object Loading : StatsScreenState
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
@@ -13,5 +13,5 @@ sealed class StatsScreenState {
 | 
			
		||||
        val titles: StatsData.Titles,
 | 
			
		||||
        val chapters: StatsData.Chapters,
 | 
			
		||||
        val trackers: StatsData.Trackers,
 | 
			
		||||
    ) : StatsScreenState()
 | 
			
		||||
    ) : StatsScreenState
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,28 @@
 | 
			
		||||
package eu.kanade.presentation.more.stats.data
 | 
			
		||||
 | 
			
		||||
sealed class StatsData {
 | 
			
		||||
sealed interface StatsData {
 | 
			
		||||
 | 
			
		||||
    data class Overview(
 | 
			
		||||
        val libraryMangaCount: Int,
 | 
			
		||||
        val completedMangaCount: Int,
 | 
			
		||||
        val totalReadDuration: Long,
 | 
			
		||||
    ) : StatsData()
 | 
			
		||||
    ) : StatsData
 | 
			
		||||
 | 
			
		||||
    data class Titles(
 | 
			
		||||
        val globalUpdateItemCount: Int,
 | 
			
		||||
        val startedMangaCount: Int,
 | 
			
		||||
        val localMangaCount: Int,
 | 
			
		||||
    ) : StatsData()
 | 
			
		||||
    ) : StatsData
 | 
			
		||||
 | 
			
		||||
    data class Chapters(
 | 
			
		||||
        val totalChapterCount: Int,
 | 
			
		||||
        val readChapterCount: Int,
 | 
			
		||||
        val downloadCount: Int,
 | 
			
		||||
    ) : StatsData()
 | 
			
		||||
    ) : StatsData
 | 
			
		||||
 | 
			
		||||
    data class Trackers(
 | 
			
		||||
        val trackedTitleCount: Int,
 | 
			
		||||
        val meanScore: Double,
 | 
			
		||||
        val trackerCount: Int,
 | 
			
		||||
    ) : StatsData()
 | 
			
		||||
    ) : StatsData
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import eu.kanade.presentation.manga.components.MangaBottomActionMenu
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
 | 
			
		||||
import eu.kanade.tachiyomi.ui.updates.UpdatesState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
 | 
			
		||||
@@ -40,7 +40,7 @@ import kotlin.time.Duration.Companion.seconds
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun UpdateScreen(
 | 
			
		||||
    state: UpdatesState,
 | 
			
		||||
    state: UpdatesScreenModel.State,
 | 
			
		||||
    snackbarHostState: SnackbarHostState,
 | 
			
		||||
    lastUpdated: Long,
 | 
			
		||||
    relativeTime: Int,
 | 
			
		||||
@@ -209,7 +209,7 @@ private fun UpdatesBottomBar(
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class UpdatesUiModel {
 | 
			
		||||
    data class Header(val date: String) : UpdatesUiModel()
 | 
			
		||||
    data class Item(val item: UpdatesItem) : UpdatesUiModel()
 | 
			
		||||
sealed interface UpdatesUiModel {
 | 
			
		||||
    data class Header(val date: String) : UpdatesUiModel
 | 
			
		||||
    data class Item(val item: UpdatesItem) : UpdatesUiModel
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -152,8 +152,8 @@ sealed class Image(
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class Location {
 | 
			
		||||
    data class Pictures private constructor(val relativePath: String) : Location() {
 | 
			
		||||
sealed interface Location {
 | 
			
		||||
    data class Pictures private constructor(val relativePath: String) : Location {
 | 
			
		||||
        companion object {
 | 
			
		||||
            fun create(relativePath: String = ""): Pictures {
 | 
			
		||||
                return Pictures(relativePath)
 | 
			
		||||
@@ -161,7 +161,7 @@ sealed class Location {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data object Cache : Location()
 | 
			
		||||
    data object Cache : Location
 | 
			
		||||
 | 
			
		||||
    fun directory(context: Context): File {
 | 
			
		||||
        return when (this) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.model
 | 
			
		||||
 | 
			
		||||
sealed class LoadResult {
 | 
			
		||||
    data class Success(val extension: Extension.Installed) : LoadResult()
 | 
			
		||||
    data class Untrusted(val extension: Extension.Untrusted) : LoadResult()
 | 
			
		||||
    data object Error : LoadResult()
 | 
			
		||||
sealed interface LoadResult {
 | 
			
		||||
    data class Success(val extension: Extension.Installed) : LoadResult
 | 
			
		||||
    data class Untrusted(val extension: Extension.Untrusted) : LoadResult
 | 
			
		||||
    data object Error : LoadResult
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -54,20 +54,20 @@ class ExtensionFilterScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class ExtensionFilterEvent {
 | 
			
		||||
    data object FailedFetchingLanguages : ExtensionFilterEvent()
 | 
			
		||||
sealed interface ExtensionFilterEvent {
 | 
			
		||||
    data object FailedFetchingLanguages : ExtensionFilterEvent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class ExtensionFilterState {
 | 
			
		||||
sealed interface ExtensionFilterState {
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data object Loading : ExtensionFilterState()
 | 
			
		||||
    data object Loading : ExtensionFilterState
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val languages: List<String>,
 | 
			
		||||
        val enabledLanguages: Set<String> = emptySet(),
 | 
			
		||||
    ) : ExtensionFilterState() {
 | 
			
		||||
    ) : ExtensionFilterState {
 | 
			
		||||
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
            get() = languages.isEmpty()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.extension
 | 
			
		||||
 | 
			
		||||
import android.app.Application
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import cafe.adriel.voyager.core.model.StateScreenModel
 | 
			
		||||
import cafe.adriel.voyager.core.model.coroutineScope
 | 
			
		||||
import eu.kanade.domain.extension.interactor.GetExtensionsByType
 | 
			
		||||
@@ -35,7 +36,7 @@ class ExtensionsScreenModel(
 | 
			
		||||
    preferences: SourcePreferences = Injekt.get(),
 | 
			
		||||
    private val extensionManager: ExtensionManager = Injekt.get(),
 | 
			
		||||
    private val getExtensions: GetExtensionsByType = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<ExtensionsState>(ExtensionsState()) {
 | 
			
		||||
) : StateScreenModel<ExtensionsScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
 | 
			
		||||
 | 
			
		||||
@@ -190,16 +191,17 @@ class ExtensionsScreenModel(
 | 
			
		||||
    fun trustSignature(signatureHash: String) {
 | 
			
		||||
        extensionManager.trustSignature(signatureHash)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class ExtensionsState(
 | 
			
		||||
    val isLoading: Boolean = true,
 | 
			
		||||
    val isRefreshing: Boolean = false,
 | 
			
		||||
    val items: ItemGroups = mutableMapOf(),
 | 
			
		||||
    val updates: Int = 0,
 | 
			
		||||
    val searchQuery: String? = null,
 | 
			
		||||
) {
 | 
			
		||||
    val isEmpty = items.isEmpty()
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val isRefreshing: Boolean = false,
 | 
			
		||||
        val items: ItemGroups = mutableMapOf(),
 | 
			
		||||
        val updates: Int = 0,
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
        val isEmpty = items.isEmpty()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typealias ItemGroups = MutableMap<ExtensionUiModel.Header, List<ExtensionUiModel.Item>>
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ class ExtensionDetailsScreenModel(
 | 
			
		||||
    private val extensionManager: ExtensionManager = Injekt.get(),
 | 
			
		||||
    private val getExtensionSources: GetExtensionSources = Injekt.get(),
 | 
			
		||||
    private val toggleSource: ToggleSource = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<ExtensionDetailsState>(ExtensionDetailsState()) {
 | 
			
		||||
) : StateScreenModel<ExtensionDetailsScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private val _events: Channel<ExtensionDetailsEvent> = Channel()
 | 
			
		||||
    val events: Flow<ExtensionDetailsEvent> = _events.receiveAsFlow()
 | 
			
		||||
@@ -160,21 +160,21 @@ class ExtensionDetailsScreenModel(
 | 
			
		||||
            url + "/src/" + pkgName.replace(".", "/") + path
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val extension: Extension.Installed? = null,
 | 
			
		||||
        private val _sources: List<ExtensionSourceItem>? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        val sources: List<ExtensionSourceItem>
 | 
			
		||||
            get() = _sources.orEmpty()
 | 
			
		||||
 | 
			
		||||
        val isLoading: Boolean
 | 
			
		||||
            get() = extension == null || _sources == null
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class ExtensionDetailsEvent {
 | 
			
		||||
    data object Uninstalled : ExtensionDetailsEvent()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Immutable
 | 
			
		||||
data class ExtensionDetailsState(
 | 
			
		||||
    val extension: Extension.Installed? = null,
 | 
			
		||||
    private val _sources: List<ExtensionSourceItem>? = null,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    val sources: List<ExtensionSourceItem>
 | 
			
		||||
        get() = _sources.orEmpty()
 | 
			
		||||
 | 
			
		||||
    val isLoading: Boolean
 | 
			
		||||
        get() = extension == null || _sources == null
 | 
			
		||||
sealed interface ExtensionDetailsEvent {
 | 
			
		||||
    data object Uninstalled : ExtensionDetailsEvent
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.toast
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import tachiyomi.presentation.core.screens.LoadingScreen
 | 
			
		||||
 | 
			
		||||
data class MigrationMangaScreen(
 | 
			
		||||
data class MigrateMangaScreen(
 | 
			
		||||
    private val sourceId: Long,
 | 
			
		||||
) : Screen() {
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +25,7 @@ data class MigrationMangaScreen(
 | 
			
		||||
    override fun Content() {
 | 
			
		||||
        val context = LocalContext.current
 | 
			
		||||
        val navigator = LocalNavigator.currentOrThrow
 | 
			
		||||
        val screenModel = rememberScreenModel { MigrationMangaScreenModel(sourceId) }
 | 
			
		||||
        val screenModel = rememberScreenModel { MigrateMangaScreenModel(sourceId) }
 | 
			
		||||
 | 
			
		||||
        val state by screenModel.state.collectAsState()
 | 
			
		||||
 | 
			
		||||
@@ -20,11 +20,11 @@ import tachiyomi.domain.source.service.SourceManager
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class MigrationMangaScreenModel(
 | 
			
		||||
class MigrateMangaScreenModel(
 | 
			
		||||
    private val sourceId: Long,
 | 
			
		||||
    private val sourceManager: SourceManager = Injekt.get(),
 | 
			
		||||
    private val getFavorites: GetFavorites = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<MigrateMangaState>(MigrateMangaState()) {
 | 
			
		||||
) : StateScreenModel<MigrateMangaScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private val _events: Channel<MigrationMangaEvent> = Channel()
 | 
			
		||||
    val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow()
 | 
			
		||||
@@ -51,24 +51,24 @@ class MigrationMangaScreenModel(
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val source: Source? = null,
 | 
			
		||||
        private val titleList: List<Manga>? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        val titles: List<Manga>
 | 
			
		||||
            get() = titleList.orEmpty()
 | 
			
		||||
 | 
			
		||||
        val isLoading: Boolean
 | 
			
		||||
            get() = source == null || titleList == null
 | 
			
		||||
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
            get() = titles.isEmpty()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class MigrationMangaEvent {
 | 
			
		||||
    data object FailedFetchingFavorites : MigrationMangaEvent()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Immutable
 | 
			
		||||
data class MigrateMangaState(
 | 
			
		||||
    val source: Source? = null,
 | 
			
		||||
    private val titleList: List<Manga>? = null,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    val titles: List<Manga>
 | 
			
		||||
        get() = titleList.orEmpty()
 | 
			
		||||
 | 
			
		||||
    val isLoading: Boolean
 | 
			
		||||
        get() = source == null || titleList == null
 | 
			
		||||
 | 
			
		||||
    val isEmpty: Boolean
 | 
			
		||||
        get() = titles.isEmpty()
 | 
			
		||||
sealed interface MigrationMangaEvent {
 | 
			
		||||
    data object FailedFetchingFavorites : MigrationMangaEvent
 | 
			
		||||
}
 | 
			
		||||
@@ -37,7 +37,7 @@ class MigrateSearchScreenDialogScreenModel(
 | 
			
		||||
        val dialog: Dialog? = null,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data class Migrate(val manga: Manga) : Dialog()
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data class Migrate(val manga: Manga) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.browse.migration.sources
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import cafe.adriel.voyager.core.model.StateScreenModel
 | 
			
		||||
import cafe.adriel.voyager.core.model.coroutineScope
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
 | 
			
		||||
@@ -23,7 +24,7 @@ class MigrateSourceScreenModel(
 | 
			
		||||
    preferences: SourcePreferences = Injekt.get(),
 | 
			
		||||
    private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(),
 | 
			
		||||
    private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<MigrateSourceState>(MigrateSourceState()) {
 | 
			
		||||
) : StateScreenModel<MigrateSourceScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private val _channel = Channel<Event>(Int.MAX_VALUE)
 | 
			
		||||
    val channel = _channel.receiveAsFlow()
 | 
			
		||||
@@ -76,16 +77,17 @@ class MigrateSourceScreenModel(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        data object FailedFetchingSourcesWithCount : Event()
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val items: List<Pair<Source, Long>> = emptyList(),
 | 
			
		||||
        val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
 | 
			
		||||
        val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
 | 
			
		||||
    ) {
 | 
			
		||||
        val isEmpty = items.isEmpty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed interface Event {
 | 
			
		||||
        data object FailedFetchingSourcesWithCount : Event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class MigrateSourceState(
 | 
			
		||||
    val isLoading: Boolean = true,
 | 
			
		||||
    val items: List<Pair<Source, Long>> = emptyList(),
 | 
			
		||||
    val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
 | 
			
		||||
    val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
 | 
			
		||||
) {
 | 
			
		||||
    val isEmpty = items.isEmpty()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import eu.kanade.presentation.browse.MigrateSourceScreen
 | 
			
		||||
import eu.kanade.presentation.components.AppBar
 | 
			
		||||
import eu.kanade.presentation.components.TabContent
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreen
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun Screen.migrateSourceTab(): TabContent {
 | 
			
		||||
@@ -40,7 +40,7 @@ fun Screen.migrateSourceTab(): TabContent {
 | 
			
		||||
                state = state,
 | 
			
		||||
                contentPadding = contentPadding,
 | 
			
		||||
                onClickItem = { source ->
 | 
			
		||||
                    navigator.push(MigrationMangaScreen(source.id))
 | 
			
		||||
                    navigator.push(MigrateMangaScreen(source.id))
 | 
			
		||||
                },
 | 
			
		||||
                onToggleSortingDirection = screenModel::toggleSortingDirection,
 | 
			
		||||
                onToggleSortingMode = screenModel::toggleSortingMode,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,12 @@ class SourcesFilterScreen : Screen() {
 | 
			
		||||
        val screenModel = rememberScreenModel { SourcesFilterScreenModel() }
 | 
			
		||||
        val state by screenModel.state.collectAsState()
 | 
			
		||||
 | 
			
		||||
        if (state is SourcesFilterState.Loading) {
 | 
			
		||||
        if (state is SourcesFilterScreenModel.State.Loading) {
 | 
			
		||||
            LoadingScreen()
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (state is SourcesFilterState.Error) {
 | 
			
		||||
        if (state is SourcesFilterScreenModel.State.Error) {
 | 
			
		||||
            val context = LocalContext.current
 | 
			
		||||
            LaunchedEffect(Unit) {
 | 
			
		||||
                context.toast(R.string.internal_error)
 | 
			
		||||
@@ -36,7 +36,7 @@ class SourcesFilterScreen : Screen() {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val successState = state as SourcesFilterState.Success
 | 
			
		||||
        val successState = state as SourcesFilterScreenModel.State.Success
 | 
			
		||||
 | 
			
		||||
        SourcesFilterScreen(
 | 
			
		||||
            navigateUp = navigator::pop,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.browse.source
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import cafe.adriel.voyager.core.model.StateScreenModel
 | 
			
		||||
import cafe.adriel.voyager.core.model.coroutineScope
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
 | 
			
		||||
@@ -21,7 +22,7 @@ class SourcesFilterScreenModel(
 | 
			
		||||
    private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
 | 
			
		||||
    private val toggleSource: ToggleSource = Injekt.get(),
 | 
			
		||||
    private val toggleLanguage: ToggleLanguage = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<SourcesFilterState>(SourcesFilterState.Loading) {
 | 
			
		||||
) : StateScreenModel<SourcesFilterScreenModel.State>(State.Loading) {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        coroutineScope.launch {
 | 
			
		||||
@@ -32,14 +33,14 @@ class SourcesFilterScreenModel(
 | 
			
		||||
            ) { a, b, c -> Triple(a, b, c) }
 | 
			
		||||
                .catch { throwable ->
 | 
			
		||||
                    mutableState.update {
 | 
			
		||||
                        SourcesFilterState.Error(
 | 
			
		||||
                        State.Error(
 | 
			
		||||
                            throwable = throwable,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .collectLatest { (languagesWithSources, enabledLanguages, disabledSources) ->
 | 
			
		||||
                    mutableState.update {
 | 
			
		||||
                        SourcesFilterState.Success(
 | 
			
		||||
                        State.Success(
 | 
			
		||||
                            items = languagesWithSources,
 | 
			
		||||
                            enabledLanguages = enabledLanguages,
 | 
			
		||||
                            disabledSources = disabledSources,
 | 
			
		||||
@@ -56,23 +57,26 @@ class SourcesFilterScreenModel(
 | 
			
		||||
    fun toggleLanguage(language: String) {
 | 
			
		||||
        toggleLanguage.await(language)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class SourcesFilterState {
 | 
			
		||||
    sealed interface State {
 | 
			
		||||
 | 
			
		||||
    data object Loading : SourcesFilterState()
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data object Loading : State
 | 
			
		||||
 | 
			
		||||
    data class Error(
 | 
			
		||||
        val throwable: Throwable,
 | 
			
		||||
    ) : SourcesFilterState()
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data class Error(
 | 
			
		||||
            val throwable: Throwable,
 | 
			
		||||
        ) : State
 | 
			
		||||
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val items: SortedMap<String, List<Source>>,
 | 
			
		||||
        val enabledLanguages: Set<String>,
 | 
			
		||||
        val disabledSources: Set<String>,
 | 
			
		||||
    ) : SourcesFilterState() {
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data class Success(
 | 
			
		||||
            val items: SortedMap<String, List<Source>>,
 | 
			
		||||
            val enabledLanguages: Set<String>,
 | 
			
		||||
            val disabledSources: Set<String>,
 | 
			
		||||
        ) : State {
 | 
			
		||||
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
            get() = items.isEmpty()
 | 
			
		||||
            val isEmpty: Boolean
 | 
			
		||||
                get() = items.isEmpty()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,8 +93,8 @@ class SourcesScreenModel(
 | 
			
		||||
        mutableState.update { it.copy(dialog = null) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        data object FailedFetchingSources : Event()
 | 
			
		||||
    sealed interface Event {
 | 
			
		||||
        data object FailedFetchingSources : Event
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class Dialog(val source: Source)
 | 
			
		||||
 
 | 
			
		||||
@@ -365,15 +365,15 @@ class BrowseSourceScreenModel(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data object Filter : Dialog()
 | 
			
		||||
        data class RemoveManga(val manga: Manga) : Dialog()
 | 
			
		||||
        data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog()
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data object Filter : Dialog
 | 
			
		||||
        data class RemoveManga(val manga: Manga) : Dialog
 | 
			
		||||
        data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
 | 
			
		||||
        data class ChangeMangaCategory(
 | 
			
		||||
            val manga: Manga,
 | 
			
		||||
            val initialSelection: List<CheckboxState.State<Category>>,
 | 
			
		||||
        ) : Dialog()
 | 
			
		||||
        data class Migrate(val newManga: Manga) : Dialog()
 | 
			
		||||
        ) : Dialog
 | 
			
		||||
        data class Migrate(val newManga: Manga) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
 
 | 
			
		||||
@@ -190,16 +190,16 @@ enum class SourceFilter {
 | 
			
		||||
    PinnedOnly,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class SearchItemResult {
 | 
			
		||||
    data object Loading : SearchItemResult()
 | 
			
		||||
sealed interface SearchItemResult {
 | 
			
		||||
    data object Loading : SearchItemResult
 | 
			
		||||
 | 
			
		||||
    data class Error(
 | 
			
		||||
        val throwable: Throwable,
 | 
			
		||||
    ) : SearchItemResult()
 | 
			
		||||
    ) : SearchItemResult
 | 
			
		||||
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val result: List<Manga>,
 | 
			
		||||
    ) : SearchItemResult() {
 | 
			
		||||
    ) : SearchItemResult {
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
            get() = result.isEmpty()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -107,27 +107,27 @@ class CategoryScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class CategoryDialog {
 | 
			
		||||
    data object Create : CategoryDialog()
 | 
			
		||||
    data class Rename(val category: Category) : CategoryDialog()
 | 
			
		||||
    data class Delete(val category: Category) : CategoryDialog()
 | 
			
		||||
sealed interface CategoryDialog {
 | 
			
		||||
    data object Create : CategoryDialog
 | 
			
		||||
    data class Rename(val category: Category) : CategoryDialog
 | 
			
		||||
    data class Delete(val category: Category) : CategoryDialog
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class CategoryEvent {
 | 
			
		||||
    sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent()
 | 
			
		||||
sealed interface CategoryEvent {
 | 
			
		||||
    sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent
 | 
			
		||||
    data object InternalError : LocalizedMessage(R.string.internal_error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class CategoryScreenState {
 | 
			
		||||
sealed interface CategoryScreenState {
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data object Loading : CategoryScreenState()
 | 
			
		||||
    data object Loading : CategoryScreenState
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val categories: List<Category>,
 | 
			
		||||
        val dialog: CategoryDialog? = null,
 | 
			
		||||
    ) : CategoryScreenState() {
 | 
			
		||||
    ) : CategoryScreenState {
 | 
			
		||||
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
            get() = categories.isEmpty()
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ class HistoryScreenModel(
 | 
			
		||||
    private val getHistory: GetHistory = Injekt.get(),
 | 
			
		||||
    private val getNextChapters: GetNextChapters = Injekt.get(),
 | 
			
		||||
    private val removeHistory: RemoveHistory = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<HistoryState>(HistoryState()) {
 | 
			
		||||
) : StateScreenModel<HistoryScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
 | 
			
		||||
    val events: Flow<Event> = _events.receiveAsFlow()
 | 
			
		||||
@@ -113,21 +113,21 @@ class HistoryScreenModel(
 | 
			
		||||
        mutableState.update { it.copy(dialog = dialog) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data object DeleteAll : Dialog()
 | 
			
		||||
        data class Delete(val history: HistoryWithRelations) : Dialog()
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
        val list: List<HistoryUiModel>? = null,
 | 
			
		||||
        val dialog: Dialog? = null,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data object DeleteAll : Dialog
 | 
			
		||||
        data class Delete(val history: HistoryWithRelations) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        data class OpenChapter(val chapter: Chapter?) : Event()
 | 
			
		||||
        data object InternalError : Event()
 | 
			
		||||
        data object HistoryCleared : Event()
 | 
			
		||||
    sealed interface Event {
 | 
			
		||||
        data class OpenChapter(val chapter: Chapter?) : Event
 | 
			
		||||
        data object InternalError : Event
 | 
			
		||||
        data object HistoryCleared : Event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Immutable
 | 
			
		||||
data class HistoryState(
 | 
			
		||||
    val searchQuery: String? = null,
 | 
			
		||||
    val list: List<HistoryUiModel>? = null,
 | 
			
		||||
    val dialog: HistoryScreenModel.Dialog? = null,
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -291,11 +291,11 @@ object HomeScreen : Screen() {
 | 
			
		||||
        showBottomNavEvent.send(show)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Tab {
 | 
			
		||||
        data class Library(val mangaIdToOpen: Long? = null) : Tab()
 | 
			
		||||
        data object Updates : Tab()
 | 
			
		||||
        data object History : Tab()
 | 
			
		||||
        data class Browse(val toExtensions: Boolean = false) : Tab()
 | 
			
		||||
        data class More(val toDownloads: Boolean) : Tab()
 | 
			
		||||
    sealed interface Tab {
 | 
			
		||||
        data class Library(val mangaIdToOpen: Long? = null) : Tab
 | 
			
		||||
        data object Updates : Tab
 | 
			
		||||
        data object History : Tab
 | 
			
		||||
        data class Browse(val toExtensions: Boolean = false) : Tab
 | 
			
		||||
        data class More(val toDownloads: Boolean) : Tab
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -657,10 +657,10 @@ class LibraryScreenModel(
 | 
			
		||||
        mutableState.update { it.copy(dialog = null) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data object SettingsSheet : Dialog()
 | 
			
		||||
        data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog()
 | 
			
		||||
        data class DeleteManga(val manga: List<Manga>) : Dialog()
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data object SettingsSheet : Dialog
 | 
			
		||||
        data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog
 | 
			
		||||
        data class DeleteManga(val manga: List<Manga>) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
 
 | 
			
		||||
@@ -75,12 +75,12 @@ class MangaScreen(
 | 
			
		||||
 | 
			
		||||
        val state by screenModel.state.collectAsState()
 | 
			
		||||
 | 
			
		||||
        if (state is MangaScreenState.Loading) {
 | 
			
		||||
        if (state is MangaScreenModel.State.Loading) {
 | 
			
		||||
            LoadingScreen()
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val successState = state as MangaScreenState.Success
 | 
			
		||||
        val successState = state as MangaScreenModel.State.Success
 | 
			
		||||
        val isHttpSource = remember { successState.source is HttpSource }
 | 
			
		||||
 | 
			
		||||
        LaunchedEffect(successState.manga, screenModel.source) {
 | 
			
		||||
 
 | 
			
		||||
@@ -98,10 +98,10 @@ class MangaScreenModel(
 | 
			
		||||
    private val getTracks: GetTracks = Injekt.get(),
 | 
			
		||||
    private val setMangaCategories: SetMangaCategories = Injekt.get(),
 | 
			
		||||
    val snackbarHostState: SnackbarHostState = SnackbarHostState(),
 | 
			
		||||
) : StateScreenModel<MangaScreenState>(MangaScreenState.Loading) {
 | 
			
		||||
) : StateScreenModel<MangaScreenModel.State>(State.Loading) {
 | 
			
		||||
 | 
			
		||||
    private val successState: MangaScreenState.Success?
 | 
			
		||||
        get() = state.value as? MangaScreenState.Success
 | 
			
		||||
    private val successState: State.Success?
 | 
			
		||||
        get() = state.value as? State.Success
 | 
			
		||||
 | 
			
		||||
    private val loggedServices by lazy { trackManager.services.filter { it.isLogged } }
 | 
			
		||||
 | 
			
		||||
@@ -133,11 +133,11 @@ class MangaScreenModel(
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper function to update the UI state only if it's currently in success state
 | 
			
		||||
     */
 | 
			
		||||
    private inline fun updateSuccessState(func: (MangaScreenState.Success) -> MangaScreenState.Success) {
 | 
			
		||||
    private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
 | 
			
		||||
        mutableState.update {
 | 
			
		||||
            when (it) {
 | 
			
		||||
                MangaScreenState.Loading -> it
 | 
			
		||||
                is MangaScreenState.Success -> func(it)
 | 
			
		||||
                State.Loading -> it
 | 
			
		||||
                is State.Success -> func(it)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -175,7 +175,7 @@ class MangaScreenModel(
 | 
			
		||||
 | 
			
		||||
            // Show what we have earlier
 | 
			
		||||
            mutableState.update {
 | 
			
		||||
                MangaScreenState.Success(
 | 
			
		||||
                State.Success(
 | 
			
		||||
                    manga = manga,
 | 
			
		||||
                    source = Injekt.get<SourceManager>().getOrStub(manga.source),
 | 
			
		||||
                    isFromSource = isFromSource,
 | 
			
		||||
@@ -334,8 +334,7 @@ class MangaScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun promptChangeCategories() {
 | 
			
		||||
        val state = successState ?: return
 | 
			
		||||
        val manga = state.manga
 | 
			
		||||
        val manga = successState?.manga ?: return
 | 
			
		||||
        coroutineScope.launch {
 | 
			
		||||
            val categories = getCategories()
 | 
			
		||||
            val selection = getMangaCategoryIds(manga)
 | 
			
		||||
@@ -662,9 +661,9 @@ class MangaScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markPreviousChapterRead(pointer: Chapter) {
 | 
			
		||||
        val successState = successState ?: return
 | 
			
		||||
        val manga = successState?.manga ?: return
 | 
			
		||||
        val chapters = filteredChapters.orEmpty().map { it.chapter }
 | 
			
		||||
        val prevChapters = if (successState.manga.sortDescending()) chapters.asReversed() else chapters
 | 
			
		||||
        val prevChapters = if (manga.sortDescending()) chapters.asReversed() else chapters
 | 
			
		||||
        val pointerPos = prevChapters.indexOf(pointer)
 | 
			
		||||
        if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true)
 | 
			
		||||
    }
 | 
			
		||||
@@ -940,13 +939,13 @@ class MangaScreenModel(
 | 
			
		||||
 | 
			
		||||
    // Track sheet - end
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog()
 | 
			
		||||
        data class DeleteChapters(val chapters: List<Chapter>) : Dialog()
 | 
			
		||||
        data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog()
 | 
			
		||||
        data object SettingsSheet : Dialog()
 | 
			
		||||
        data object TrackSheet : Dialog()
 | 
			
		||||
        data object FullCover : Dialog()
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
 | 
			
		||||
        data class DeleteChapters(val chapters: List<Chapter>) : Dialog
 | 
			
		||||
        data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
 | 
			
		||||
        data object SettingsSheet : Dialog
 | 
			
		||||
        data object TrackSheet : Dialog
 | 
			
		||||
        data object FullCover : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun dismissDialog() {
 | 
			
		||||
@@ -968,48 +967,48 @@ class MangaScreenModel(
 | 
			
		||||
    fun showCoverDialog() {
 | 
			
		||||
        updateSuccessState { it.copy(dialog = Dialog.FullCover) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class MangaScreenState {
 | 
			
		||||
    @Immutable
 | 
			
		||||
    object Loading : MangaScreenState()
 | 
			
		||||
    sealed interface State {
 | 
			
		||||
        @Immutable
 | 
			
		||||
        object Loading : State
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val manga: Manga,
 | 
			
		||||
        val source: Source,
 | 
			
		||||
        val isFromSource: Boolean,
 | 
			
		||||
        val chapters: List<ChapterItem>,
 | 
			
		||||
        val trackItems: List<TrackItem> = emptyList(),
 | 
			
		||||
        val isRefreshingData: Boolean = false,
 | 
			
		||||
        val dialog: MangaScreenModel.Dialog? = null,
 | 
			
		||||
        val hasPromptedToAddBefore: Boolean = false,
 | 
			
		||||
    ) : MangaScreenState() {
 | 
			
		||||
        @Immutable
 | 
			
		||||
        data class Success(
 | 
			
		||||
            val manga: Manga,
 | 
			
		||||
            val source: Source,
 | 
			
		||||
            val isFromSource: Boolean,
 | 
			
		||||
            val chapters: List<ChapterItem>,
 | 
			
		||||
            val trackItems: List<TrackItem> = emptyList(),
 | 
			
		||||
            val isRefreshingData: Boolean = false,
 | 
			
		||||
            val dialog: Dialog? = null,
 | 
			
		||||
            val hasPromptedToAddBefore: Boolean = false,
 | 
			
		||||
        ) : State {
 | 
			
		||||
 | 
			
		||||
        val processedChapters by lazy {
 | 
			
		||||
            chapters.applyFilters(manga).toList()
 | 
			
		||||
        }
 | 
			
		||||
            val processedChapters by lazy {
 | 
			
		||||
                chapters.applyFilters(manga).toList()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        val trackingAvailable: Boolean
 | 
			
		||||
            get() = trackItems.isNotEmpty()
 | 
			
		||||
            val trackingAvailable: Boolean
 | 
			
		||||
                get() = trackItems.isNotEmpty()
 | 
			
		||||
 | 
			
		||||
        val trackingCount: Int
 | 
			
		||||
            get() = trackItems.count { it.track != null }
 | 
			
		||||
            val trackingCount: Int
 | 
			
		||||
                get() = trackItems.count { it.track != null }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Applies the view filters to the list of chapters obtained from the database.
 | 
			
		||||
         * @return an observable of the list of chapters filtered and sorted.
 | 
			
		||||
         */
 | 
			
		||||
        private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
 | 
			
		||||
            val isLocalManga = manga.isLocal()
 | 
			
		||||
            val unreadFilter = manga.unreadFilter
 | 
			
		||||
            val downloadedFilter = manga.downloadedFilter
 | 
			
		||||
            val bookmarkedFilter = manga.bookmarkedFilter
 | 
			
		||||
            return asSequence()
 | 
			
		||||
                .filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
 | 
			
		||||
                .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
 | 
			
		||||
                .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
 | 
			
		||||
                .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
 | 
			
		||||
            /**
 | 
			
		||||
             * Applies the view filters to the list of chapters obtained from the database.
 | 
			
		||||
             * @return an observable of the list of chapters filtered and sorted.
 | 
			
		||||
             */
 | 
			
		||||
            private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
 | 
			
		||||
                val isLocalManga = manga.isLocal()
 | 
			
		||||
                val unreadFilter = manga.unreadFilter
 | 
			
		||||
                val downloadedFilter = manga.downloadedFilter
 | 
			
		||||
                val bookmarkedFilter = manga.bookmarkedFilter
 | 
			
		||||
                return asSequence()
 | 
			
		||||
                    .filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
 | 
			
		||||
                    .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
 | 
			
		||||
                    .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
 | 
			
		||||
                    .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -108,8 +108,8 @@ private class MoreScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class DownloadQueueState {
 | 
			
		||||
    data object Stopped : DownloadQueueState()
 | 
			
		||||
    data class Paused(val pending: Int) : DownloadQueueState()
 | 
			
		||||
    data class Downloading(val pending: Int) : DownloadQueueState()
 | 
			
		||||
sealed interface DownloadQueueState {
 | 
			
		||||
    data object Stopped : DownloadQueueState
 | 
			
		||||
    data class Paused(val pending: Int) : DownloadQueueState
 | 
			
		||||
    data class Downloading(val pending: Int) : DownloadQueueState
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -798,9 +798,9 @@ class ReaderViewModel(
 | 
			
		||||
        Error,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class SaveImageResult {
 | 
			
		||||
        class Success(val uri: Uri) : SaveImageResult()
 | 
			
		||||
        class Error(val error: Throwable) : SaveImageResult()
 | 
			
		||||
    sealed interface SaveImageResult {
 | 
			
		||||
        class Success(val uri: Uri) : SaveImageResult
 | 
			
		||||
        class Error(val error: Throwable) : SaveImageResult
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -860,18 +860,18 @@ class ReaderViewModel(
 | 
			
		||||
            get() = viewerChapters?.currChapter?.pages?.size ?: -1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data object Loading : Dialog()
 | 
			
		||||
        data object Settings : Dialog()
 | 
			
		||||
        data class PageActions(val page: ReaderPage) : Dialog()
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data object Loading : Dialog
 | 
			
		||||
        data object Settings : Dialog
 | 
			
		||||
        data class PageActions(val page: ReaderPage) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        data object ReloadViewerChapters : Event()
 | 
			
		||||
        data class SetOrientation(val orientation: Int) : Event()
 | 
			
		||||
        data class SetCoverResult(val result: SetAsCoverResult) : Event()
 | 
			
		||||
    sealed interface Event {
 | 
			
		||||
        data object ReloadViewerChapters : Event
 | 
			
		||||
        data class SetOrientation(val orientation: Int) : Event
 | 
			
		||||
        data class SetCoverResult(val result: SetAsCoverResult) : Event
 | 
			
		||||
 | 
			
		||||
        data class SavedImage(val result: SaveImageResult) : Event()
 | 
			
		||||
        data class ShareImage(val uri: Uri, val page: ReaderPage) : Event()
 | 
			
		||||
        data class SavedImage(val result: SaveImageResult) : Event
 | 
			
		||||
        data class ShareImage(val uri: Uri, val page: ReaderPage) : Event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,10 +39,10 @@ data class ReaderChapter(val chapter: Chapter) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class State {
 | 
			
		||||
        data object Wait : State()
 | 
			
		||||
        data object Loading : State()
 | 
			
		||||
        data class Error(val error: Throwable) : State()
 | 
			
		||||
        data class Loaded(val pages: List<ReaderPage>) : State()
 | 
			
		||||
    sealed interface State {
 | 
			
		||||
        data object Wait : State
 | 
			
		||||
        data object Loading : State
 | 
			
		||||
        data class Error(val error: Throwable) : State
 | 
			
		||||
        data class Loaded(val pages: List<ReaderPage>) : State
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ class UpdatesScreenModel(
 | 
			
		||||
    private val libraryPreferences: LibraryPreferences = Injekt.get(),
 | 
			
		||||
    val snackbarHostState: SnackbarHostState = SnackbarHostState(),
 | 
			
		||||
    uiPreferences: UiPreferences = Injekt.get(),
 | 
			
		||||
) : StateScreenModel<UpdatesState>(UpdatesState()) {
 | 
			
		||||
) : StateScreenModel<UpdatesScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
 | 
			
		||||
    val events: Flow<Event> = _events.receiveAsFlow()
 | 
			
		||||
@@ -366,46 +366,46 @@ class UpdatesScreenModel(
 | 
			
		||||
        libraryPreferences.newUpdatesCount().set(0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Dialog {
 | 
			
		||||
        data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog()
 | 
			
		||||
    }
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val items: List<UpdatesItem> = emptyList(),
 | 
			
		||||
        val dialog: UpdatesScreenModel.Dialog? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
        val selected = items.filter { it.selected }
 | 
			
		||||
        val selectionMode = selected.isNotEmpty()
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        data object InternalError : Event()
 | 
			
		||||
        data class LibraryUpdateTriggered(val started: Boolean) : Event()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
        fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
 | 
			
		||||
            val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
 | 
			
		||||
 | 
			
		||||
@Immutable
 | 
			
		||||
data class UpdatesState(
 | 
			
		||||
    val isLoading: Boolean = true,
 | 
			
		||||
    val items: List<UpdatesItem> = emptyList(),
 | 
			
		||||
    val dialog: UpdatesScreenModel.Dialog? = null,
 | 
			
		||||
) {
 | 
			
		||||
    val selected = items.filter { it.selected }
 | 
			
		||||
    val selectionMode = selected.isNotEmpty()
 | 
			
		||||
 | 
			
		||||
    fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
 | 
			
		||||
        val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
 | 
			
		||||
 | 
			
		||||
        return items
 | 
			
		||||
            .map { UpdatesUiModel.Item(it) }
 | 
			
		||||
            .insertSeparators { before, after ->
 | 
			
		||||
                val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
 | 
			
		||||
                val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
 | 
			
		||||
                when {
 | 
			
		||||
                    beforeDate.time != afterDate.time && afterDate.time != 0L -> {
 | 
			
		||||
                        val text = afterDate.toRelativeString(
 | 
			
		||||
                            context = context,
 | 
			
		||||
                            range = relativeTime,
 | 
			
		||||
                            dateFormat = dateFormat,
 | 
			
		||||
                        )
 | 
			
		||||
                        UpdatesUiModel.Header(text)
 | 
			
		||||
            return items
 | 
			
		||||
                .map { UpdatesUiModel.Item(it) }
 | 
			
		||||
                .insertSeparators { before, after ->
 | 
			
		||||
                    val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
 | 
			
		||||
                    val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
 | 
			
		||||
                    when {
 | 
			
		||||
                        beforeDate.time != afterDate.time && afterDate.time != 0L -> {
 | 
			
		||||
                            val text = afterDate.toRelativeString(
 | 
			
		||||
                                context = context,
 | 
			
		||||
                                range = relativeTime,
 | 
			
		||||
                                dateFormat = dateFormat,
 | 
			
		||||
                            )
 | 
			
		||||
                            UpdatesUiModel.Header(text)
 | 
			
		||||
                        }
 | 
			
		||||
                        // Return null to avoid adding a separator between two items.
 | 
			
		||||
                        else -> null
 | 
			
		||||
                    }
 | 
			
		||||
                    // Return null to avoid adding a separator between two items.
 | 
			
		||||
                    else -> null
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed interface Dialog {
 | 
			
		||||
        data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed interface Event {
 | 
			
		||||
        data object InternalError : Event
 | 
			
		||||
        data class LibraryUpdateTriggered(val started: Boolean) : Event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user