mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Use immutable collections in more places
This commit is contained in:
		@@ -175,6 +175,7 @@ dependencies {
 | 
			
		||||
    implementation(libs.bundles.sqlite)
 | 
			
		||||
 | 
			
		||||
    implementation(kotlinx.reflect)
 | 
			
		||||
    implementation(kotlinx.immutables)
 | 
			
		||||
 | 
			
		||||
    implementation(platform(kotlinx.coroutines.bom))
 | 
			
		||||
    implementation(kotlinx.bundles.coroutines)
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import eu.kanade.presentation.components.AppBar
 | 
			
		||||
import eu.kanade.presentation.util.formattedMessage
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.coroutines.flow.StateFlow
 | 
			
		||||
import tachiyomi.domain.library.model.LibraryDisplayMode
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
@@ -76,7 +77,7 @@ fun BrowseSourceContent(
 | 
			
		||||
            modifier = Modifier.padding(contentPadding),
 | 
			
		||||
            message = getErrorMessage(errorState),
 | 
			
		||||
            actions = if (source is LocalSource) {
 | 
			
		||||
                listOf(
 | 
			
		||||
                persistentListOf(
 | 
			
		||||
                    EmptyScreenAction(
 | 
			
		||||
                        stringResId = R.string.local_source_help_guide,
 | 
			
		||||
                        icon = Icons.Outlined.HelpOutline,
 | 
			
		||||
@@ -84,7 +85,7 @@ fun BrowseSourceContent(
 | 
			
		||||
                    ),
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
                listOf(
 | 
			
		||||
                persistentListOf(
 | 
			
		||||
                    EmptyScreenAction(
 | 
			
		||||
                        stringResId = R.string.action_retry,
 | 
			
		||||
                        icon = Icons.Outlined.Refresh,
 | 
			
		||||
 
 | 
			
		||||
@@ -94,8 +94,8 @@ fun SourcesScreen(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun SourceHeader(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    language: String,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
) {
 | 
			
		||||
    val context = LocalContext.current
 | 
			
		||||
    Text(
 | 
			
		||||
@@ -108,11 +108,11 @@ private fun SourceHeader(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun SourceItem(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    source: Source,
 | 
			
		||||
    onClickItem: (Source, Listing) -> Unit,
 | 
			
		||||
    onLongClickItem: (Source) -> Unit,
 | 
			
		||||
    onClickPin: (Source) -> Unit,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
) {
 | 
			
		||||
    BaseSourceItem(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
 | 
			
		||||
import eu.kanade.presentation.theme.TachiyomiTheme
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import tachiyomi.presentation.core.screens.EmptyScreen
 | 
			
		||||
import tachiyomi.presentation.core.screens.EmptyScreenAction
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +31,7 @@ private fun WithActionPreview() {
 | 
			
		||||
        Surface {
 | 
			
		||||
            EmptyScreen(
 | 
			
		||||
                textResource = R.string.empty_screen,
 | 
			
		||||
                actions = listOf(
 | 
			
		||||
                actions = persistentListOf(
 | 
			
		||||
                    EmptyScreenAction(
 | 
			
		||||
                        stringResId = R.string.action_retry,
 | 
			
		||||
                        icon = Icons.Outlined.Refresh,
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import tachiyomi.domain.manga.interactor.FetchInterval
 | 
			
		||||
import tachiyomi.presentation.core.components.WheelTextPicker
 | 
			
		||||
 | 
			
		||||
@@ -67,13 +68,15 @@ fun SetIntervalDialog(
 | 
			
		||||
                contentAlignment = Alignment.Center,
 | 
			
		||||
            ) {
 | 
			
		||||
                val size = DpSize(width = maxWidth / 2, height = 128.dp)
 | 
			
		||||
                val items = (0..FetchInterval.MAX_INTERVAL).map {
 | 
			
		||||
                    if (it == 0) {
 | 
			
		||||
                        stringResource(R.string.label_default)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        it.toString()
 | 
			
		||||
                val items = (0..FetchInterval.MAX_INTERVAL)
 | 
			
		||||
                    .map {
 | 
			
		||||
                        if (it == 0) {
 | 
			
		||||
                            stringResource(R.string.label_default)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            it.toString()
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                    .toImmutableList()
 | 
			
		||||
                WheelTextPicker(
 | 
			
		||||
                    items = items,
 | 
			
		||||
                    size = size,
 | 
			
		||||
 
 | 
			
		||||
@@ -85,12 +85,11 @@ fun PreferenceScreen(
 | 
			
		||||
private fun List<Preference>.findHighlightedIndex(highlightKey: String): Int {
 | 
			
		||||
    return flatMap {
 | 
			
		||||
        if (it is Preference.PreferenceGroup) {
 | 
			
		||||
            mutableListOf<String?>()
 | 
			
		||||
                .apply {
 | 
			
		||||
                    add(null) // Header
 | 
			
		||||
                    addAll(it.preferenceItems.map { groupItem -> groupItem.title })
 | 
			
		||||
                    add(null) // Spacer
 | 
			
		||||
                }
 | 
			
		||||
            buildList<String?> {
 | 
			
		||||
                add(null) // Header
 | 
			
		||||
                addAll(it.preferenceItems.map { groupItem -> groupItem.title })
 | 
			
		||||
                add(null) // Spacer
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            listOf(it.title)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,10 @@ import eu.kanade.tachiyomi.data.backup.BackupCreateJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.toast
 | 
			
		||||
import kotlinx.collections.immutable.PersistentSet
 | 
			
		||||
import kotlinx.collections.immutable.minus
 | 
			
		||||
import kotlinx.collections.immutable.plus
 | 
			
		||||
import kotlinx.collections.immutable.toPersistentSet
 | 
			
		||||
import kotlinx.coroutines.flow.update
 | 
			
		||||
import tachiyomi.presentation.core.components.LabeledCheckbox
 | 
			
		||||
import tachiyomi.presentation.core.components.material.Scaffold
 | 
			
		||||
@@ -154,7 +158,7 @@ private class CreateBackupScreenModel : StateScreenModel<CreateBackupScreenModel
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val flags: Set<Int> = BackupChoices.keys,
 | 
			
		||||
        val flags: PersistentSet<Int> = BackupChoices.keys.toPersistentSet(),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.presentation.theme.TachiyomiTheme
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
 | 
			
		||||
import tachiyomi.presentation.core.components.WheelNumberPicker
 | 
			
		||||
import tachiyomi.presentation.core.components.WheelTextPicker
 | 
			
		||||
@@ -102,7 +104,7 @@ fun TrackChapterSelector(
 | 
			
		||||
        title = stringResource(R.string.chapters),
 | 
			
		||||
        content = {
 | 
			
		||||
            WheelNumberPicker(
 | 
			
		||||
                items = range.toList(),
 | 
			
		||||
                items = range.toImmutableList(),
 | 
			
		||||
                modifier = Modifier.align(Alignment.Center),
 | 
			
		||||
                startIndex = selection,
 | 
			
		||||
                onSelectionChanged = { onSelectionChange(it) },
 | 
			
		||||
@@ -117,7 +119,7 @@ fun TrackChapterSelector(
 | 
			
		||||
fun TrackScoreSelector(
 | 
			
		||||
    selection: String,
 | 
			
		||||
    onSelectionChange: (String) -> Unit,
 | 
			
		||||
    selections: List<String>,
 | 
			
		||||
    selections: ImmutableList<String>,
 | 
			
		||||
    onConfirm: () -> Unit,
 | 
			
		||||
    onDismissRequest: () -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import androidx.annotation.DrawableRes
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
 | 
			
		||||
interface Tracker {
 | 
			
		||||
@@ -36,7 +37,7 @@ interface Tracker {
 | 
			
		||||
 | 
			
		||||
    fun getCompletionStatus(): Int
 | 
			
		||||
 | 
			
		||||
    fun getScoreList(): List<String>
 | 
			
		||||
    fun getScoreList(): ImmutableList<String>
 | 
			
		||||
 | 
			
		||||
    // TODO: Store all scores as 10 point in the future maybe?
 | 
			
		||||
    fun get10PointScore(track: tachiyomi.domain.track.model.Track): Double
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,9 @@ import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.DeletableTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -74,18 +77,18 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> {
 | 
			
		||||
        return when (scorePreference.get()) {
 | 
			
		||||
            // 10 point
 | 
			
		||||
            POINT_10 -> IntRange(0, 10).map(Int::toString)
 | 
			
		||||
            POINT_10 -> IntRange(0, 10).map(Int::toString).toImmutableList()
 | 
			
		||||
            // 100 point
 | 
			
		||||
            POINT_100 -> IntRange(0, 100).map(Int::toString)
 | 
			
		||||
            POINT_100 -> IntRange(0, 100).map(Int::toString).toImmutableList()
 | 
			
		||||
            // 5 stars
 | 
			
		||||
            POINT_5 -> IntRange(0, 5).map { "$it ★" }
 | 
			
		||||
            POINT_5 -> IntRange(0, 5).map { "$it ★" }.toImmutableList()
 | 
			
		||||
            // Smiley
 | 
			
		||||
            POINT_3 -> listOf("-", "😦", "😐", "😊")
 | 
			
		||||
            POINT_3 -> persistentListOf("-", "😦", "😐", "😊")
 | 
			
		||||
            // 10 point decimal
 | 
			
		||||
            POINT_10_DECIMAL -> IntRange(0, 100).map { (it / 10f).toString() }
 | 
			
		||||
            POINT_10_DECIMAL -> IntRange(0, 100).map { (it / 10f).toString() }.toImmutableList()
 | 
			
		||||
            else -> throw Exception("Unknown score type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -18,9 +20,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
 | 
			
		||||
 | 
			
		||||
    private val api by lazy { BangumiApi(id, client, interceptor) }
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
        return IntRange(0, 10).map(Int::toString)
 | 
			
		||||
    }
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = SCORE_LIST
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String {
 | 
			
		||||
        return track.score.toInt().toString()
 | 
			
		||||
@@ -141,5 +141,9 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
 | 
			
		||||
        const val ON_HOLD = 4
 | 
			
		||||
        const val DROPPED = 5
 | 
			
		||||
        const val PLAN_TO_READ = 1
 | 
			
		||||
 | 
			
		||||
        private val SCORE_LIST = IntRange(0, 10)
 | 
			
		||||
            .map(Int::toString)
 | 
			
		||||
            .toImmutableList()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import eu.kanade.tachiyomi.source.ConfigurableSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.sourcePreferences
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
import tachiyomi.domain.source.service.SourceManager
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -51,7 +53,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> = emptyList()
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = persistentListOf()
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String = ""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.DeletableTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -54,9 +56,9 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> {
 | 
			
		||||
        val df = DecimalFormat("0.#")
 | 
			
		||||
        return listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }
 | 
			
		||||
        return (listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }).toImmutableList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun indexToScore(index: Int): Float {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import okhttp3.Dns
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
@@ -48,7 +50,7 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> = emptyList()
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = persistentListOf()
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String = ""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@ import eu.kanade.tachiyomi.data.track.DeletableTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.copyTo
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
 | 
			
		||||
class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +20,12 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
 | 
			
		||||
        const val COMPLETE_LIST = 2
 | 
			
		||||
        const val UNFINISHED_LIST = 3
 | 
			
		||||
        const val ON_HOLD_LIST = 4
 | 
			
		||||
 | 
			
		||||
        private val SCORE_LIST = (
 | 
			
		||||
            (0..9)
 | 
			
		||||
                .flatMap { i -> (0..9).map { j -> "$i.$j" } } + listOf("10.0")
 | 
			
		||||
            )
 | 
			
		||||
            .toImmutableList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val interceptor by lazy { MangaUpdatesInterceptor(this) }
 | 
			
		||||
@@ -48,11 +56,9 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETE_LIST
 | 
			
		||||
 | 
			
		||||
    private val _scoreList = (0..9).flatMap { i -> (0..9).map { j -> "$i.$j" } } + listOf("10.0")
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = SCORE_LIST
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> = _scoreList
 | 
			
		||||
 | 
			
		||||
    override fun indexToScore(index: Int): Float = _scoreList[index].toFloat()
 | 
			
		||||
    override fun indexToScore(index: Int): Float = SCORE_LIST[index].toFloat()
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String = track.score.toString()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.DeletableTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -23,6 +25,10 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
        private const val SEARCH_ID_PREFIX = "id:"
 | 
			
		||||
        private const val SEARCH_LIST_PREFIX = "my:"
 | 
			
		||||
 | 
			
		||||
        private val SCORE_LIST = IntRange(0, 10)
 | 
			
		||||
            .map(Int::toString)
 | 
			
		||||
            .toImmutableList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val json: Json by injectLazy()
 | 
			
		||||
@@ -57,9 +63,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
        return IntRange(0, 10).map(Int::toString)
 | 
			
		||||
    }
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = SCORE_LIST
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String {
 | 
			
		||||
        return track.score.toInt().toString()
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.DeletableTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -20,6 +22,10 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
 | 
			
		||||
        const val DROPPED = 4
 | 
			
		||||
        const val PLAN_TO_READ = 5
 | 
			
		||||
        const val REREADING = 6
 | 
			
		||||
 | 
			
		||||
        private val SCORE_LIST = IntRange(0, 10)
 | 
			
		||||
            .map(Int::toString)
 | 
			
		||||
            .toImmutableList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val json: Json by injectLazy()
 | 
			
		||||
@@ -28,9 +34,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
 | 
			
		||||
 | 
			
		||||
    private val api by lazy { ShikimoriApi(id, client, interceptor) }
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
        return IntRange(0, 10).map(Int::toString)
 | 
			
		||||
    }
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = SCORE_LIST
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String {
 | 
			
		||||
        return track.score.toInt().toString()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import eu.kanade.tachiyomi.data.track.BaseTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga as DomainManga
 | 
			
		||||
import tachiyomi.domain.track.model.Track as DomainTrack
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +43,7 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = COMPLETED
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> = emptyList()
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = persistentListOf()
 | 
			
		||||
 | 
			
		||||
    override fun displayScore(track: Track): String = ""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,11 @@ import cafe.adriel.voyager.core.model.screenModelScope
 | 
			
		||||
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleLanguage
 | 
			
		||||
import eu.kanade.domain.source.service.SourcePreferences
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableSet
 | 
			
		||||
import kotlinx.collections.immutable.persistentSetOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableSet
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
@@ -41,8 +46,8 @@ class ExtensionFilterScreenModel(
 | 
			
		||||
                .collectLatest { (extensionLanguages, enabledLanguages) ->
 | 
			
		||||
                    mutableState.update {
 | 
			
		||||
                        ExtensionFilterState.Success(
 | 
			
		||||
                            languages = extensionLanguages,
 | 
			
		||||
                            enabledLanguages = enabledLanguages,
 | 
			
		||||
                            languages = extensionLanguages.toImmutableList(),
 | 
			
		||||
                            enabledLanguages = enabledLanguages.toImmutableSet(),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -65,8 +70,8 @@ sealed interface ExtensionFilterState {
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val languages: List<String>,
 | 
			
		||||
        val enabledLanguages: Set<String> = emptySet(),
 | 
			
		||||
        val languages: ImmutableList<String>,
 | 
			
		||||
        val enabledLanguages: ImmutableSet<String> = persistentSetOf(),
 | 
			
		||||
    ) : ExtensionFilterState {
 | 
			
		||||
 | 
			
		||||
        val isEmpty: Boolean
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,9 @@ import eu.kanade.tachiyomi.extension.model.Extension
 | 
			
		||||
import eu.kanade.tachiyomi.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
@@ -75,10 +78,10 @@ class ExtensionDetailsScreenModel(
 | 
			
		||||
                        }
 | 
			
		||||
                        .catch { throwable ->
 | 
			
		||||
                            logcat(LogPriority.ERROR, throwable)
 | 
			
		||||
                            mutableState.update { it.copy(_sources = emptyList()) }
 | 
			
		||||
                            mutableState.update { it.copy(_sources = persistentListOf()) }
 | 
			
		||||
                        }
 | 
			
		||||
                        .collectLatest { sources ->
 | 
			
		||||
                            mutableState.update { it.copy(_sources = sources) }
 | 
			
		||||
                            mutableState.update { it.copy(_sources = sources.toImmutableList()) }
 | 
			
		||||
                        }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -164,11 +167,11 @@ class ExtensionDetailsScreenModel(
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val extension: Extension.Installed? = null,
 | 
			
		||||
        private val _sources: List<ExtensionSourceItem>? = null,
 | 
			
		||||
        private val _sources: ImmutableList<ExtensionSourceItem>? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        val sources: List<ExtensionSourceItem>
 | 
			
		||||
            get() = _sources.orEmpty()
 | 
			
		||||
        val sources: ImmutableList<ExtensionSourceItem>
 | 
			
		||||
            get() = _sources ?: persistentListOf()
 | 
			
		||||
 | 
			
		||||
        val isLoading: Boolean
 | 
			
		||||
            get() = extension == null || _sources == null
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,9 @@ import androidx.compose.runtime.Immutable
 | 
			
		||||
import cafe.adriel.voyager.core.model.StateScreenModel
 | 
			
		||||
import cafe.adriel.voyager.core.model.screenModelScope
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
@@ -40,11 +43,13 @@ class MigrateMangaScreenModel(
 | 
			
		||||
                    logcat(LogPriority.ERROR, it)
 | 
			
		||||
                    _events.send(MigrationMangaEvent.FailedFetchingFavorites)
 | 
			
		||||
                    mutableState.update { state ->
 | 
			
		||||
                        state.copy(titleList = emptyList())
 | 
			
		||||
                        state.copy(titleList = persistentListOf())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .map { manga ->
 | 
			
		||||
                    manga.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title })
 | 
			
		||||
                    manga
 | 
			
		||||
                        .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title })
 | 
			
		||||
                        .toImmutableList()
 | 
			
		||||
                }
 | 
			
		||||
                .collectLatest { list ->
 | 
			
		||||
                    mutableState.update { it.copy(titleList = list) }
 | 
			
		||||
@@ -55,11 +60,11 @@ class MigrateMangaScreenModel(
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val source: Source? = null,
 | 
			
		||||
        private val titleList: List<Manga>? = null,
 | 
			
		||||
        private val titleList: ImmutableList<Manga>? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        val titles: List<Manga>
 | 
			
		||||
            get() = titleList.orEmpty()
 | 
			
		||||
        val titles: ImmutableList<Manga>
 | 
			
		||||
            get() = titleList ?: persistentListOf()
 | 
			
		||||
 | 
			
		||||
        val isLoading: Boolean
 | 
			
		||||
            get() = source == null || titleList == null
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,9 @@ import cafe.adriel.voyager.core.model.screenModelScope
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
 | 
			
		||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
 | 
			
		||||
import eu.kanade.domain.source.service.SourcePreferences
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
@@ -40,7 +43,7 @@ class MigrateSourceScreenModel(
 | 
			
		||||
                    mutableState.update {
 | 
			
		||||
                        it.copy(
 | 
			
		||||
                            isLoading = false,
 | 
			
		||||
                            items = sources,
 | 
			
		||||
                            items = sources.toImmutableList(),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -80,7 +83,7 @@ class MigrateSourceScreenModel(
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val items: List<Pair<Source, Long>> = emptyList(),
 | 
			
		||||
        val items: ImmutableList<Pair<Source, Long>> = persistentListOf(),
 | 
			
		||||
        val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
 | 
			
		||||
        val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
 | 
			
		||||
    ) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,9 @@ import eu.kanade.domain.source.interactor.GetEnabledSources
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleSource
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
 | 
			
		||||
import eu.kanade.presentation.browse.SourceUiModel
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
@@ -65,14 +68,16 @@ class SourcesScreenModel(
 | 
			
		||||
 | 
			
		||||
            state.copy(
 | 
			
		||||
                isLoading = false,
 | 
			
		||||
                items = byLang.flatMap {
 | 
			
		||||
                    listOf(
 | 
			
		||||
                        SourceUiModel.Header(it.key),
 | 
			
		||||
                        *it.value.map { source ->
 | 
			
		||||
                            SourceUiModel.Item(source)
 | 
			
		||||
                        }.toTypedArray(),
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
                items = byLang
 | 
			
		||||
                    .flatMap {
 | 
			
		||||
                        listOf(
 | 
			
		||||
                            SourceUiModel.Header(it.key),
 | 
			
		||||
                            *it.value.map { source ->
 | 
			
		||||
                                SourceUiModel.Item(source)
 | 
			
		||||
                            }.toTypedArray(),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    .toImmutableList(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -103,7 +108,7 @@ class SourcesScreenModel(
 | 
			
		||||
    data class State(
 | 
			
		||||
        val dialog: Dialog? = null,
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val items: List<SourceUiModel> = emptyList(),
 | 
			
		||||
        val items: ImmutableList<SourceUiModel> = persistentListOf(),
 | 
			
		||||
    ) {
 | 
			
		||||
        val isEmpty = items.isEmpty()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,10 @@ import eu.kanade.domain.source.service.SourcePreferences
 | 
			
		||||
import eu.kanade.presentation.util.ioCoroutineScope
 | 
			
		||||
import eu.kanade.tachiyomi.extension.ExtensionManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.CatalogueSource
 | 
			
		||||
import kotlinx.collections.immutable.PersistentMap
 | 
			
		||||
import kotlinx.collections.immutable.mutate
 | 
			
		||||
import kotlinx.collections.immutable.persistentMapOf
 | 
			
		||||
import kotlinx.collections.immutable.toPersistentMap
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.asCoroutineDispatcher
 | 
			
		||||
import kotlinx.coroutines.async
 | 
			
		||||
@@ -125,9 +129,17 @@ abstract class SearchScreenModel(
 | 
			
		||||
        // Reuse previous results if possible
 | 
			
		||||
        if (sameQuery) {
 | 
			
		||||
            val existingResults = state.value.items
 | 
			
		||||
            updateItems(sources.associateWith { existingResults[it] ?: SearchItemResult.Loading })
 | 
			
		||||
            updateItems(
 | 
			
		||||
                sources
 | 
			
		||||
                    .associateWith { existingResults[it] ?: SearchItemResult.Loading }
 | 
			
		||||
                    .toPersistentMap(),
 | 
			
		||||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            updateItems(sources.associateWith { SearchItemResult.Loading })
 | 
			
		||||
            updateItems(
 | 
			
		||||
                sources
 | 
			
		||||
                    .associateWith { SearchItemResult.Loading }
 | 
			
		||||
                    .toPersistentMap(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        searchJob = ioCoroutineScope.launch {
 | 
			
		||||
@@ -160,14 +172,21 @@ abstract class SearchScreenModel(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateItems(items: Map<CatalogueSource, SearchItemResult>) {
 | 
			
		||||
        mutableState.update { it.copy(items = items.toSortedMap(sortComparator(items))) }
 | 
			
		||||
    private fun updateItems(items: PersistentMap<CatalogueSource, SearchItemResult>) {
 | 
			
		||||
        mutableState.update {
 | 
			
		||||
            it.copy(
 | 
			
		||||
                items = items
 | 
			
		||||
                    .toSortedMap(sortComparator(items))
 | 
			
		||||
                    .toPersistentMap(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateItem(source: CatalogueSource, result: SearchItemResult) {
 | 
			
		||||
        val mutableItems = state.value.items.toMutableMap()
 | 
			
		||||
        mutableItems[source] = result
 | 
			
		||||
        updateItems(mutableItems)
 | 
			
		||||
        val newItems = state.value.items.mutate {
 | 
			
		||||
            it[source] = result
 | 
			
		||||
        }
 | 
			
		||||
        updateItems(newItems)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
@@ -176,7 +195,7 @@ abstract class SearchScreenModel(
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
        val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
 | 
			
		||||
        val onlyShowHasResults: Boolean = false,
 | 
			
		||||
        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
 | 
			
		||||
        val items: PersistentMap<CatalogueSource, SearchItemResult> = persistentMapOf(),
 | 
			
		||||
    ) {
 | 
			
		||||
        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
 | 
			
		||||
        val total: Int = items.size
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ import androidx.compose.runtime.Immutable
 | 
			
		||||
import cafe.adriel.voyager.core.model.StateScreenModel
 | 
			
		||||
import cafe.adriel.voyager.core.model.screenModelScope
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.receiveAsFlow
 | 
			
		||||
@@ -36,7 +38,9 @@ class CategoryScreenModel(
 | 
			
		||||
                .collectLatest { categories ->
 | 
			
		||||
                    mutableState.update {
 | 
			
		||||
                        CategoryScreenState.Success(
 | 
			
		||||
                            categories = categories.filterNot(Category::isSystemCategory),
 | 
			
		||||
                            categories = categories
 | 
			
		||||
                                .filterNot(Category::isSystemCategory)
 | 
			
		||||
                                .toImmutableList(),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -135,7 +139,7 @@ sealed interface CategoryScreenState {
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class Success(
 | 
			
		||||
        val categories: List<Category>,
 | 
			
		||||
        val categories: ImmutableList<Category>,
 | 
			
		||||
        val dialog: CategoryDialog? = null,
 | 
			
		||||
    ) : CategoryScreenState {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,9 @@ import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.chapter.getNextUnread
 | 
			
		||||
import eu.kanade.tachiyomi.util.removeCovers
 | 
			
		||||
import kotlinx.collections.immutable.PersistentList
 | 
			
		||||
import kotlinx.collections.immutable.mutate
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.combine
 | 
			
		||||
@@ -562,16 +565,16 @@ class LibraryScreenModel(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun clearSelection() {
 | 
			
		||||
        mutableState.update { it.copy(selection = emptyList()) }
 | 
			
		||||
        mutableState.update { it.copy(selection = persistentListOf()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun toggleSelection(manga: LibraryManga) {
 | 
			
		||||
        mutableState.update { state ->
 | 
			
		||||
            val newSelection = state.selection.toMutableList().apply {
 | 
			
		||||
                if (fastAny { it.id == manga.id }) {
 | 
			
		||||
                    removeAll { it.id == manga.id }
 | 
			
		||||
            val newSelection = state.selection.mutate { list ->
 | 
			
		||||
                if (list.fastAny { it.id == manga.id }) {
 | 
			
		||||
                    list.removeAll { it.id == manga.id }
 | 
			
		||||
                } else {
 | 
			
		||||
                    add(manga)
 | 
			
		||||
                    list.add(manga)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(selection = newSelection)
 | 
			
		||||
@@ -584,11 +587,11 @@ class LibraryScreenModel(
 | 
			
		||||
     */
 | 
			
		||||
    fun toggleRangeSelection(manga: LibraryManga) {
 | 
			
		||||
        mutableState.update { state ->
 | 
			
		||||
            val newSelection = state.selection.toMutableList().apply {
 | 
			
		||||
                val lastSelected = lastOrNull()
 | 
			
		||||
            val newSelection = state.selection.mutate { list ->
 | 
			
		||||
                val lastSelected = list.lastOrNull()
 | 
			
		||||
                if (lastSelected?.category != manga.category) {
 | 
			
		||||
                    add(manga)
 | 
			
		||||
                    return@apply
 | 
			
		||||
                    list.add(manga)
 | 
			
		||||
                    return@mutate
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                val items = state.getLibraryItemsByCategoryId(manga.category)
 | 
			
		||||
@@ -596,17 +599,17 @@ class LibraryScreenModel(
 | 
			
		||||
                val lastMangaIndex = items.indexOf(lastSelected)
 | 
			
		||||
                val curMangaIndex = items.indexOf(manga)
 | 
			
		||||
 | 
			
		||||
                val selectedIds = fastMap { it.id }
 | 
			
		||||
                val selectedIds = list.fastMap { it.id }
 | 
			
		||||
                val selectionRange = when {
 | 
			
		||||
                    lastMangaIndex < curMangaIndex -> IntRange(lastMangaIndex, curMangaIndex)
 | 
			
		||||
                    curMangaIndex < lastMangaIndex -> IntRange(curMangaIndex, lastMangaIndex)
 | 
			
		||||
                    // We shouldn't reach this point
 | 
			
		||||
                    else -> return@apply
 | 
			
		||||
                    else -> return@mutate
 | 
			
		||||
                }
 | 
			
		||||
                val newSelections = selectionRange.mapNotNull { index ->
 | 
			
		||||
                    items[index].takeUnless { it.id in selectedIds }
 | 
			
		||||
                }
 | 
			
		||||
                addAll(newSelections)
 | 
			
		||||
                list.addAll(newSelections)
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(selection = newSelection)
 | 
			
		||||
        }
 | 
			
		||||
@@ -614,14 +617,14 @@ class LibraryScreenModel(
 | 
			
		||||
 | 
			
		||||
    fun selectAll(index: Int) {
 | 
			
		||||
        mutableState.update { state ->
 | 
			
		||||
            val newSelection = state.selection.toMutableList().apply {
 | 
			
		||||
            val newSelection = state.selection.mutate { list ->
 | 
			
		||||
                val categoryId = state.categories.getOrNull(index)?.id ?: -1
 | 
			
		||||
                val selectedIds = fastMap { it.id }
 | 
			
		||||
                val selectedIds = list.fastMap { it.id }
 | 
			
		||||
                state.getLibraryItemsByCategoryId(categoryId)
 | 
			
		||||
                    ?.fastMapNotNull { item ->
 | 
			
		||||
                        item.libraryManga.takeUnless { it.id in selectedIds }
 | 
			
		||||
                    }
 | 
			
		||||
                    ?.let { addAll(it) }
 | 
			
		||||
                    ?.let { list.addAll(it) }
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(selection = newSelection)
 | 
			
		||||
        }
 | 
			
		||||
@@ -629,14 +632,14 @@ class LibraryScreenModel(
 | 
			
		||||
 | 
			
		||||
    fun invertSelection(index: Int) {
 | 
			
		||||
        mutableState.update { state ->
 | 
			
		||||
            val newSelection = state.selection.toMutableList().apply {
 | 
			
		||||
            val newSelection = state.selection.mutate { list ->
 | 
			
		||||
                val categoryId = state.categories[index].id
 | 
			
		||||
                val items = state.getLibraryItemsByCategoryId(categoryId)?.fastMap { it.libraryManga }.orEmpty()
 | 
			
		||||
                val selectedIds = fastMap { it.id }
 | 
			
		||||
                val selectedIds = list.fastMap { it.id }
 | 
			
		||||
                val (toRemove, toAdd) = items.fastPartition { it.id in selectedIds }
 | 
			
		||||
                val toRemoveIds = toRemove.fastMap { it.id }
 | 
			
		||||
                removeAll { it.id in toRemoveIds }
 | 
			
		||||
                addAll(toAdd)
 | 
			
		||||
                list.removeAll { it.id in toRemoveIds }
 | 
			
		||||
                list.addAll(toAdd)
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(selection = newSelection)
 | 
			
		||||
        }
 | 
			
		||||
@@ -703,7 +706,7 @@ class LibraryScreenModel(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val library: LibraryMap = emptyMap(),
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
        val selection: List<LibraryManga> = emptyList(),
 | 
			
		||||
        val selection: PersistentList<LibraryManga> = persistentListOf(),
 | 
			
		||||
        val hasActiveFilters: Boolean = false,
 | 
			
		||||
        val showCategoryTabs: Boolean = false,
 | 
			
		||||
        val showMangaCount: Boolean = false,
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.receiveAsFlow
 | 
			
		||||
@@ -154,7 +155,7 @@ object LibraryTab : Tab {
 | 
			
		||||
                    EmptyScreen(
 | 
			
		||||
                        textResource = R.string.information_empty_library,
 | 
			
		||||
                        modifier = Modifier.padding(contentPadding),
 | 
			
		||||
                        actions = listOf(
 | 
			
		||||
                        actions = persistentListOf(
 | 
			
		||||
                            EmptyScreenAction(
 | 
			
		||||
                                stringResId = R.string.getting_started_guide,
 | 
			
		||||
                                icon = Icons.Outlined.HelpOutline,
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.openInBrowser
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.toast
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
@@ -398,7 +399,7 @@ private data class TrackScoreSelectorScreen(
 | 
			
		||||
        private val tracker: Tracker,
 | 
			
		||||
    ) : StateScreenModel<Model.State>(State(tracker.displayScore(track.toDbTrack()))) {
 | 
			
		||||
 | 
			
		||||
        fun getSelections(): List<String> {
 | 
			
		||||
        fun getSelections(): ImmutableList<String> {
 | 
			
		||||
            return tracker.getScoreList()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,10 @@ import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.toDateKey
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
 | 
			
		||||
import kotlinx.collections.immutable.PersistentList
 | 
			
		||||
import kotlinx.collections.immutable.mutate
 | 
			
		||||
import kotlinx.collections.immutable.persistentListOf
 | 
			
		||||
import kotlinx.collections.immutable.toPersistentList
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
@@ -106,27 +110,29 @@ class UpdatesScreenModel(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> {
 | 
			
		||||
        return this.map { update ->
 | 
			
		||||
            val activeDownload = downloadManager.getQueuedDownloadOrNull(update.chapterId)
 | 
			
		||||
            val downloaded = downloadManager.isChapterDownloaded(
 | 
			
		||||
                update.chapterName,
 | 
			
		||||
                update.scanlator,
 | 
			
		||||
                update.mangaTitle,
 | 
			
		||||
                update.sourceId,
 | 
			
		||||
            )
 | 
			
		||||
            val downloadState = when {
 | 
			
		||||
                activeDownload != null -> activeDownload.status
 | 
			
		||||
                downloaded -> Download.State.DOWNLOADED
 | 
			
		||||
                else -> Download.State.NOT_DOWNLOADED
 | 
			
		||||
    private fun List<UpdatesWithRelations>.toUpdateItems(): PersistentList<UpdatesItem> {
 | 
			
		||||
        return this
 | 
			
		||||
            .map { update ->
 | 
			
		||||
                val activeDownload = downloadManager.getQueuedDownloadOrNull(update.chapterId)
 | 
			
		||||
                val downloaded = downloadManager.isChapterDownloaded(
 | 
			
		||||
                    update.chapterName,
 | 
			
		||||
                    update.scanlator,
 | 
			
		||||
                    update.mangaTitle,
 | 
			
		||||
                    update.sourceId,
 | 
			
		||||
                )
 | 
			
		||||
                val downloadState = when {
 | 
			
		||||
                    activeDownload != null -> activeDownload.status
 | 
			
		||||
                    downloaded -> Download.State.DOWNLOADED
 | 
			
		||||
                    else -> Download.State.NOT_DOWNLOADED
 | 
			
		||||
                }
 | 
			
		||||
                UpdatesItem(
 | 
			
		||||
                    update = update,
 | 
			
		||||
                    downloadStateProvider = { downloadState },
 | 
			
		||||
                    downloadProgressProvider = { activeDownload?.progress ?: 0 },
 | 
			
		||||
                    selected = update.chapterId in selectedChapterIds,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            UpdatesItem(
 | 
			
		||||
                update = update,
 | 
			
		||||
                downloadStateProvider = { downloadState },
 | 
			
		||||
                downloadProgressProvider = { activeDownload?.progress ?: 0 },
 | 
			
		||||
                selected = update.chapterId in selectedChapterIds,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
            .toPersistentList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateLibrary(): Boolean {
 | 
			
		||||
@@ -144,17 +150,14 @@ class UpdatesScreenModel(
 | 
			
		||||
     */
 | 
			
		||||
    private fun updateDownloadState(download: Download) {
 | 
			
		||||
        mutableState.update { state ->
 | 
			
		||||
            val newItems = state.items.toMutableList().apply {
 | 
			
		||||
                val modifiedIndex = indexOfFirst { it.update.chapterId == download.chapter.id }
 | 
			
		||||
                if (modifiedIndex < 0) return@apply
 | 
			
		||||
            val newItems = state.items.mutate { list ->
 | 
			
		||||
                val modifiedIndex = list.indexOfFirst { it.update.chapterId == download.chapter.id }
 | 
			
		||||
                if (modifiedIndex < 0) return@mutate
 | 
			
		||||
 | 
			
		||||
                val item = get(modifiedIndex)
 | 
			
		||||
                set(
 | 
			
		||||
                    modifiedIndex,
 | 
			
		||||
                    item.copy(
 | 
			
		||||
                        downloadStateProvider = { download.status },
 | 
			
		||||
                        downloadProgressProvider = { download.progress },
 | 
			
		||||
                    ),
 | 
			
		||||
                val item = list[modifiedIndex]
 | 
			
		||||
                list[modifiedIndex] = item.copy(
 | 
			
		||||
                    downloadStateProvider = { download.status },
 | 
			
		||||
                    downloadProgressProvider = { download.progress },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(items = newItems)
 | 
			
		||||
@@ -330,7 +333,7 @@ class UpdatesScreenModel(
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(items = newItems)
 | 
			
		||||
            state.copy(items = newItems.toPersistentList())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -340,7 +343,7 @@ class UpdatesScreenModel(
 | 
			
		||||
                selectedChapterIds.addOrRemove(it.update.chapterId, selected)
 | 
			
		||||
                it.copy(selected = selected)
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(items = newItems)
 | 
			
		||||
            state.copy(items = newItems.toPersistentList())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        selectedPositions[0] = -1
 | 
			
		||||
@@ -353,7 +356,7 @@ class UpdatesScreenModel(
 | 
			
		||||
                selectedChapterIds.addOrRemove(it.update.chapterId, !it.selected)
 | 
			
		||||
                it.copy(selected = !it.selected)
 | 
			
		||||
            }
 | 
			
		||||
            state.copy(items = newItems)
 | 
			
		||||
            state.copy(items = newItems.toPersistentList())
 | 
			
		||||
        }
 | 
			
		||||
        selectedPositions[0] = -1
 | 
			
		||||
        selectedPositions[1] = -1
 | 
			
		||||
@@ -370,7 +373,7 @@ class UpdatesScreenModel(
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val isLoading: Boolean = true,
 | 
			
		||||
        val items: List<UpdatesItem> = emptyList(),
 | 
			
		||||
        val items: PersistentList<UpdatesItem> = persistentListOf(),
 | 
			
		||||
        val dialog: Dialog? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
        val selected = items.filter { it.selected }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@ import android.graphics.Color
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.Tracker
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.collections.immutable.toImmutableList
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import tachiyomi.domain.track.model.Track
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +20,7 @@ data class DummyTracker(
 | 
			
		||||
    val valReadingStatus: Int = 1,
 | 
			
		||||
    val valRereadingStatus: Int = 1,
 | 
			
		||||
    val valCompletionStatus: Int = 2,
 | 
			
		||||
    val valScoreList: List<String> = (0..10).map(Int::toString),
 | 
			
		||||
    val valScoreList: ImmutableList<String> = (0..10).map(Int::toString).toImmutableList(),
 | 
			
		||||
    val val10PointScore: Double = 5.4,
 | 
			
		||||
    val valSearchResults: List<TrackSearch> = listOf(),
 | 
			
		||||
) : Tracker {
 | 
			
		||||
@@ -48,7 +50,7 @@ data class DummyTracker(
 | 
			
		||||
 | 
			
		||||
    override fun getCompletionStatus(): Int = valCompletionStatus
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> = valScoreList
 | 
			
		||||
    override fun getScoreList(): ImmutableList<String> = valScoreList
 | 
			
		||||
 | 
			
		||||
    override fun get10PointScore(track: Track): Double = val10PointScore
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ class EpubFile(file: File) : Closeable {
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns all the pages from the epub.
 | 
			
		||||
     */
 | 
			
		||||
    fun getPagesFromDocument(document: Document): List<String> {
 | 
			
		||||
    private fun getPagesFromDocument(document: Document): List<String> {
 | 
			
		||||
        val pages = document.select("manifest > item")
 | 
			
		||||
            .filter { node -> "application/xhtml+xml" == node.attr("media-type") }
 | 
			
		||||
            .associateBy { it.attr("id") }
 | 
			
		||||
@@ -102,10 +102,9 @@ class EpubFile(file: File) : Closeable {
 | 
			
		||||
            val imageBasePath = getParentDirectory(entryPath)
 | 
			
		||||
 | 
			
		||||
            document.allElements.forEach {
 | 
			
		||||
                if (it.tagName() == "img") {
 | 
			
		||||
                    result.add(resolveZipPath(imageBasePath, it.attr("src")))
 | 
			
		||||
                } else if (it.tagName() == "image") {
 | 
			
		||||
                    result.add(resolveZipPath(imageBasePath, it.attr("xlink:href")))
 | 
			
		||||
                when (it.tagName()) {
 | 
			
		||||
                    "img" -> result.add(resolveZipPath(imageBasePath, it.attr("src")))
 | 
			
		||||
                    "image" -> result.add(resolveZipPath(imageBasePath, it.attr("xlink:href")))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,8 @@ dependencies {
 | 
			
		||||
    implementation(compose.ui.tooling.preview)
 | 
			
		||||
    implementation(compose.ui.util)
 | 
			
		||||
    lintChecks(compose.lintchecks)
 | 
			
		||||
 | 
			
		||||
    implementation(kotlinx.immutables)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,13 +36,14 @@ fun BadgeGroup(
 | 
			
		||||
@Composable
 | 
			
		||||
fun Badge(
 | 
			
		||||
    text: String,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    color: Color = MaterialTheme.colorScheme.secondary,
 | 
			
		||||
    textColor: Color = MaterialTheme.colorScheme.onSecondary,
 | 
			
		||||
    shape: Shape = RectangleShape,
 | 
			
		||||
) {
 | 
			
		||||
    Text(
 | 
			
		||||
        text = text,
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
            .clip(shape)
 | 
			
		||||
            .background(color)
 | 
			
		||||
            .padding(horizontal = 3.dp, vertical = 1.dp),
 | 
			
		||||
@@ -56,6 +57,7 @@ fun Badge(
 | 
			
		||||
@Composable
 | 
			
		||||
fun Badge(
 | 
			
		||||
    imageVector: ImageVector,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    color: Color = MaterialTheme.colorScheme.secondary,
 | 
			
		||||
    iconColor: Color = MaterialTheme.colorScheme.onSecondary,
 | 
			
		||||
    shape: Shape = RectangleShape,
 | 
			
		||||
@@ -86,7 +88,7 @@ fun Badge(
 | 
			
		||||
    Text(
 | 
			
		||||
        text = text,
 | 
			
		||||
        inlineContent = inlineContent,
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
        modifier = modifier
 | 
			
		||||
            .clip(shape)
 | 
			
		||||
            .background(color)
 | 
			
		||||
            .padding(horizontal = 3.dp, vertical = 1.dp),
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ import androidx.compose.ui.text.input.TextFieldValue
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.unit.DpSize
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.drop
 | 
			
		||||
@@ -54,7 +55,7 @@ import kotlin.math.absoluteValue
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WheelNumberPicker(
 | 
			
		||||
    items: List<Number>,
 | 
			
		||||
    items: ImmutableList<Number>,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startIndex: Int = 0,
 | 
			
		||||
    size: DpSize = DpSize(128.dp, 128.dp),
 | 
			
		||||
@@ -78,7 +79,7 @@ fun WheelNumberPicker(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WheelTextPicker(
 | 
			
		||||
    items: List<String>,
 | 
			
		||||
    items: ImmutableList<String>,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startIndex: Int = 0,
 | 
			
		||||
    size: DpSize = DpSize(128.dp, 128.dp),
 | 
			
		||||
@@ -101,7 +102,7 @@ fun WheelTextPicker(
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun <T> WheelPicker(
 | 
			
		||||
    items: List<T>,
 | 
			
		||||
    items: ImmutableList<T>,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    startIndex: Int = 0,
 | 
			
		||||
    size: DpSize = DpSize(128.dp, 128.dp),
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.unit.LayoutDirection
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.compose.ui.util.fastForEach
 | 
			
		||||
import kotlinx.collections.immutable.ImmutableList
 | 
			
		||||
import tachiyomi.presentation.core.components.ActionButton
 | 
			
		||||
import tachiyomi.presentation.core.components.material.padding
 | 
			
		||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
 | 
			
		||||
@@ -38,7 +39,7 @@ data class EmptyScreenAction(
 | 
			
		||||
fun EmptyScreen(
 | 
			
		||||
    @StringRes textResource: Int,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    actions: List<EmptyScreenAction>? = null,
 | 
			
		||||
    actions: ImmutableList<EmptyScreenAction>? = null,
 | 
			
		||||
) {
 | 
			
		||||
    EmptyScreen(
 | 
			
		||||
        message = stringResource(textResource),
 | 
			
		||||
@@ -51,7 +52,7 @@ fun EmptyScreen(
 | 
			
		||||
fun EmptyScreen(
 | 
			
		||||
    message: String,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    actions: List<EmptyScreenAction>? = null,
 | 
			
		||||
    actions: ImmutableList<EmptyScreenAction>? = null,
 | 
			
		||||
) {
 | 
			
		||||
    val face = remember { getRandomErrorFace() }
 | 
			
		||||
    Column(
 | 
			
		||||
@@ -98,7 +99,7 @@ fun EmptyScreen(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private val ERROR_FACES = listOf(
 | 
			
		||||
private val ErrorFaces = listOf(
 | 
			
		||||
    "(・o・;)",
 | 
			
		||||
    "Σ(ಠ_ಠ)",
 | 
			
		||||
    "ಥ_ಥ",
 | 
			
		||||
@@ -108,5 +109,5 @@ private val ERROR_FACES = listOf(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private fun getRandomErrorFace(): String {
 | 
			
		||||
    return ERROR_FACES[Random.nextInt(ERROR_FACES.size)]
 | 
			
		||||
    return ErrorFaces[Random.nextInt(ErrorFaces.size)]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user