Misc refactoring
- Abstract away relative date string building - Dedupe large update warning logic
This commit is contained in:
parent
f0a0ecfd4a
commit
3d0d5c0472
13 changed files with 88 additions and 151 deletions
|
@ -0,0 +1,38 @@
|
||||||
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
|
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun relativeDateText(
|
||||||
|
date: Long,
|
||||||
|
): String {
|
||||||
|
return relativeDateText(date = Date(date).takeIf { date > 0L })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun relativeDateText(
|
||||||
|
date: Date?,
|
||||||
|
): String {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val preferences = remember { Injekt.get<UiPreferences>() }
|
||||||
|
val relativeTime = remember { preferences.relativeTime().get() }
|
||||||
|
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
||||||
|
|
||||||
|
return date
|
||||||
|
?.toRelativeString(
|
||||||
|
context,
|
||||||
|
relativeTime,
|
||||||
|
dateFormat,
|
||||||
|
)
|
||||||
|
?: stringResource(MR.strings.not_applicable)
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
package eu.kanade.presentation.components
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
|
||||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun RelativeDateHeader(
|
|
||||||
date: Date,
|
|
||||||
relativeTime: Boolean,
|
|
||||||
dateFormat: DateFormat,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
ListGroupHeader(
|
|
||||||
modifier = modifier,
|
|
||||||
text = remember {
|
|
||||||
date.toRelativeString(
|
|
||||||
context,
|
|
||||||
relativeTime,
|
|
||||||
dateFormat,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -8,30 +8,26 @@ import androidx.compose.material.icons.outlined.DeleteSweep
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.presentation.components.AppBarTitle
|
import eu.kanade.presentation.components.AppBarTitle
|
||||||
import eu.kanade.presentation.components.RelativeDateHeader
|
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
import eu.kanade.presentation.components.SearchToolbar
|
||||||
|
import eu.kanade.presentation.components.relativeDateText
|
||||||
import eu.kanade.presentation.history.components.HistoryItem
|
import eu.kanade.presentation.history.components.HistoryItem
|
||||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.core.preference.InMemoryPreferenceStore
|
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||||
|
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -42,7 +38,6 @@ fun HistoryScreen(
|
||||||
onClickCover: (mangaId: Long) -> Unit,
|
onClickCover: (mangaId: Long) -> Unit,
|
||||||
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
||||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
||||||
preferences: UiPreferences = Injekt.get(),
|
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
|
@ -88,7 +83,6 @@ fun HistoryScreen(
|
||||||
onClickCover = { history -> onClickCover(history.mangaId) },
|
onClickCover = { history -> onClickCover(history.mangaId) },
|
||||||
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
||||||
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
||||||
preferences = preferences,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,11 +96,7 @@ private fun HistoryScreenContent(
|
||||||
onClickCover: (HistoryWithRelations) -> Unit,
|
onClickCover: (HistoryWithRelations) -> Unit,
|
||||||
onClickResume: (HistoryWithRelations) -> Unit,
|
onClickResume: (HistoryWithRelations) -> Unit,
|
||||||
onClickDelete: (HistoryWithRelations) -> Unit,
|
onClickDelete: (HistoryWithRelations) -> Unit,
|
||||||
preferences: UiPreferences,
|
|
||||||
) {
|
) {
|
||||||
val relativeTime = remember { preferences.relativeTime().get() }
|
|
||||||
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
|
||||||
|
|
||||||
FastScrollLazyColumn(
|
FastScrollLazyColumn(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
) {
|
) {
|
||||||
|
@ -122,11 +112,9 @@ private fun HistoryScreenContent(
|
||||||
) { item ->
|
) { item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
is HistoryUiModel.Header -> {
|
is HistoryUiModel.Header -> {
|
||||||
RelativeDateHeader(
|
ListGroupHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
date = item.date,
|
text = relativeDateText(item.date),
|
||||||
relativeTime = relativeTime,
|
|
||||||
dateFormat = dateFormat,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is HistoryUiModel.Item -> {
|
is HistoryUiModel.Item -> {
|
||||||
|
@ -163,17 +151,6 @@ internal fun HistoryScreenPreviews(
|
||||||
onClickCover = {},
|
onClickCover = {},
|
||||||
onClickResume = { _, _ -> run {} },
|
onClickResume = { _, _ -> run {} },
|
||||||
onDialogChange = {},
|
onDialogChange = {},
|
||||||
preferences = UiPreferences(
|
|
||||||
InMemoryPreferenceStore(
|
|
||||||
sequenceOf(
|
|
||||||
InMemoryPreferenceStore.InMemoryPreference(
|
|
||||||
key = "relative_time_v2",
|
|
||||||
data = false,
|
|
||||||
defaultValue = false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.util.fastAll
|
import androidx.compose.ui.util.fastAll
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import androidx.compose.ui.util.fastMap
|
import androidx.compose.ui.util.fastMap
|
||||||
|
import eu.kanade.presentation.components.relativeDateText
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterHeader
|
import eu.kanade.presentation.manga.components.ChapterHeader
|
||||||
import eu.kanade.presentation.manga.components.ExpandableMangaDescription
|
import eu.kanade.presentation.manga.components.ExpandableMangaDescription
|
||||||
|
@ -61,7 +62,6 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
||||||
import eu.kanade.tachiyomi.ui.manga.ChapterList
|
import eu.kanade.tachiyomi.ui.manga.ChapterList
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel
|
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.service.missingChaptersCount
|
import tachiyomi.domain.chapter.service.missingChaptersCount
|
||||||
|
@ -78,16 +78,12 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
import tachiyomi.presentation.core.util.isScrolledToEnd
|
||||||
import tachiyomi.presentation.core.util.isScrollingUp
|
import tachiyomi.presentation.core.util.isScrollingUp
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaScreen(
|
fun MangaScreen(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
fetchInterval: Int?,
|
fetchInterval: Int?,
|
||||||
dateRelativeTime: Boolean,
|
|
||||||
dateFormat: DateFormat,
|
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
@ -142,8 +138,6 @@ fun MangaScreen(
|
||||||
MangaScreenSmallImpl(
|
MangaScreenSmallImpl(
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateRelativeTime = dateRelativeTime,
|
|
||||||
dateFormat = dateFormat,
|
|
||||||
fetchInterval = fetchInterval,
|
fetchInterval = fetchInterval,
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
@ -179,10 +173,8 @@ fun MangaScreen(
|
||||||
MangaScreenLargeImpl(
|
MangaScreenLargeImpl(
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateRelativeTime = dateRelativeTime,
|
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
dateFormat = dateFormat,
|
|
||||||
fetchInterval = fetchInterval,
|
fetchInterval = fetchInterval,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
|
@ -219,8 +211,6 @@ fun MangaScreen(
|
||||||
private fun MangaScreenSmallImpl(
|
private fun MangaScreenSmallImpl(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateRelativeTime: Boolean,
|
|
||||||
dateFormat: DateFormat,
|
|
||||||
fetchInterval: Int?,
|
fetchInterval: Int?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
@ -455,8 +445,6 @@ private fun MangaScreenSmallImpl(
|
||||||
manga = state.manga,
|
manga = state.manga,
|
||||||
chapters = listItem,
|
chapters = listItem,
|
||||||
isAnyChapterSelected = chapters.fastAny { it.selected },
|
isAnyChapterSelected = chapters.fastAny { it.selected },
|
||||||
dateRelativeTime = dateRelativeTime,
|
|
||||||
dateFormat = dateFormat,
|
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
|
@ -474,8 +462,6 @@ private fun MangaScreenSmallImpl(
|
||||||
fun MangaScreenLargeImpl(
|
fun MangaScreenLargeImpl(
|
||||||
state: MangaScreenModel.State.Success,
|
state: MangaScreenModel.State.Success,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateRelativeTime: Boolean,
|
|
||||||
dateFormat: DateFormat,
|
|
||||||
fetchInterval: Int?,
|
fetchInterval: Int?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
@ -705,8 +691,6 @@ fun MangaScreenLargeImpl(
|
||||||
manga = state.manga,
|
manga = state.manga,
|
||||||
chapters = listItem,
|
chapters = listItem,
|
||||||
isAnyChapterSelected = chapters.fastAny { it.selected },
|
isAnyChapterSelected = chapters.fastAny { it.selected },
|
||||||
dateRelativeTime = dateRelativeTime,
|
|
||||||
dateFormat = dateFormat,
|
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
|
@ -768,8 +752,6 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
chapters: List<ChapterList>,
|
chapters: List<ChapterList>,
|
||||||
isAnyChapterSelected: Boolean,
|
isAnyChapterSelected: Boolean,
|
||||||
dateRelativeTime: Boolean,
|
|
||||||
dateFormat: DateFormat,
|
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
|
@ -788,7 +770,6 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
contentType = { MangaScreenItem.CHAPTER },
|
contentType = { MangaScreenItem.CHAPTER },
|
||||||
) { item ->
|
) { item ->
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
when (item) {
|
when (item) {
|
||||||
is ChapterList.MissingCount -> {
|
is ChapterList.MissingCount -> {
|
||||||
|
@ -804,15 +785,7 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
} else {
|
} else {
|
||||||
item.chapter.name
|
item.chapter.name
|
||||||
},
|
},
|
||||||
date = item.chapter.dateUpload
|
date = relativeDateText(item.chapter.dateUpload),
|
||||||
.takeIf { it > 0L }
|
|
||||||
?.let {
|
|
||||||
Date(it).toRelativeString(
|
|
||||||
context,
|
|
||||||
dateRelativeTime,
|
|
||||||
dateFormat,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
readProgress = item.chapter.lastPageRead
|
readProgress = item.chapter.lastPageRead
|
||||||
.takeIf { !item.chapter.read && it > 0L }
|
.takeIf { !item.chapter.read && it > 0L }
|
||||||
?.let {
|
?.let {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.util.fastAll
|
import androidx.compose.ui.util.fastAll
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
@ -37,6 +36,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
|
import java.util.Date
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -44,7 +44,6 @@ fun UpdateScreen(
|
||||||
state: UpdatesScreenModel.State,
|
state: UpdatesScreenModel.State,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
lastUpdated: Long,
|
lastUpdated: Long,
|
||||||
relativeTime: Boolean,
|
|
||||||
onClickCover: (UpdatesItem) -> Unit,
|
onClickCover: (UpdatesItem) -> Unit,
|
||||||
onSelectAll: (Boolean) -> Unit,
|
onSelectAll: (Boolean) -> Unit,
|
||||||
onInvertSelection: () -> Unit,
|
onInvertSelection: () -> Unit,
|
||||||
|
@ -58,8 +57,6 @@ fun UpdateScreen(
|
||||||
) {
|
) {
|
||||||
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
UpdatesAppBar(
|
UpdatesAppBar(
|
||||||
|
@ -113,7 +110,7 @@ fun UpdateScreen(
|
||||||
updatesLastUpdatedItem(lastUpdated)
|
updatesLastUpdatedItem(lastUpdated)
|
||||||
|
|
||||||
updatesUiItems(
|
updatesUiItems(
|
||||||
uiModels = state.getUiModel(context, relativeTime),
|
uiModels = state.getUiModel(),
|
||||||
selectionMode = state.selectionMode,
|
selectionMode = state.selectionMode,
|
||||||
onUpdateSelected = onUpdateSelected,
|
onUpdateSelected = onUpdateSelected,
|
||||||
onClickCover = onClickCover,
|
onClickCover = onClickCover,
|
||||||
|
@ -209,6 +206,6 @@ private fun UpdatesBottomBar(
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface UpdatesUiModel {
|
sealed interface UpdatesUiModel {
|
||||||
data class Header(val date: String) : UpdatesUiModel
|
data class Header(val date: Date) : UpdatesUiModel
|
||||||
data class Item(val item: UpdatesItem) : UpdatesUiModel
|
data class Item(val item: UpdatesItem) : UpdatesUiModel
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.components.relativeDateText
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
|
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
|
||||||
import eu.kanade.presentation.manga.components.DotSeparatorText
|
import eu.kanade.presentation.manga.components.DotSeparatorText
|
||||||
|
@ -91,7 +92,7 @@ internal fun LazyListScope.updatesUiItems(
|
||||||
is UpdatesUiModel.Header -> {
|
is UpdatesUiModel.Header -> {
|
||||||
ListGroupHeader(
|
ListGroupHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
text = item.date,
|
text = relativeDateText(item.date),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is UpdatesUiModel.Item -> {
|
is UpdatesUiModel.Item -> {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||||
|
@ -37,7 +36,6 @@ import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
@ -152,8 +150,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
*
|
*
|
||||||
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
||||||
*/
|
*/
|
||||||
private fun addMangaToQueue(categoryId: Long) {
|
private suspend fun addMangaToQueue(categoryId: Long) {
|
||||||
val libraryManga = runBlocking { getLibraryManga.await() }
|
val libraryManga = getLibraryManga.await()
|
||||||
|
|
||||||
val listToUpdate = if (categoryId != -1L) {
|
val listToUpdate = if (categoryId != -1L) {
|
||||||
libraryManga.filter { it.category == categoryId }
|
libraryManga.filter { it.category == categoryId }
|
||||||
|
@ -179,7 +177,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
|
|
||||||
val restrictions = libraryPreferences.autoUpdateMangaRestrictions().get()
|
val restrictions = libraryPreferences.autoUpdateMangaRestrictions().get()
|
||||||
val skippedUpdates = mutableListOf<Pair<Manga, String?>>()
|
val skippedUpdates = mutableListOf<Pair<Manga, String?>>()
|
||||||
val fetchWindow = fetchInterval.getWindow(ZonedDateTime.now())
|
val (_, fetchWindowUpperBound) = fetchInterval.getWindow(ZonedDateTime.now())
|
||||||
|
|
||||||
mangaToUpdate = listToUpdate
|
mangaToUpdate = listToUpdate
|
||||||
.filter {
|
.filter {
|
||||||
|
@ -206,7 +204,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions && it.manga.nextUpdate > fetchWindow.second -> {
|
MANGA_OUTSIDE_RELEASE_PERIOD in restrictions && it.manga.nextUpdate > fetchWindowUpperBound -> {
|
||||||
skippedUpdates.add(
|
skippedUpdates.add(
|
||||||
it.manga to context.stringResource(MR.strings.skipped_reason_not_in_release_period),
|
it.manga to context.stringResource(MR.strings.skipped_reason_not_in_release_period),
|
||||||
)
|
)
|
||||||
|
@ -218,14 +216,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
}
|
}
|
||||||
.sortedBy { it.manga.title }
|
.sortedBy { it.manga.title }
|
||||||
|
|
||||||
// Warn when excessively checking a single source
|
notifier.showQueueSizeWarningNotificationIfNeeded(mangaToUpdate)
|
||||||
val maxUpdatesFromSource = mangaToUpdate
|
|
||||||
.groupBy { it.manga.source }
|
|
||||||
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
|
||||||
.maxOfOrNull { it.value.size } ?: 0
|
|
||||||
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
|
||||||
notifier.showQueueSizeWarningNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skippedUpdates.isNotEmpty()) {
|
if (skippedUpdates.isNotEmpty()) {
|
||||||
// TODO: surface skipped reasons to user?
|
// TODO: surface skipped reasons to user?
|
||||||
|
|
|
@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.data.download.Downloader
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationHandler
|
import eu.kanade.tachiyomi.data.notification.NotificationHandler
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
import eu.kanade.tachiyomi.util.lang.chop
|
||||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
|
@ -30,15 +31,22 @@ import tachiyomi.core.i18n.pluralStringResource
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.util.lang.launchUI
|
import tachiyomi.core.util.lang.launchUI
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
|
||||||
class LibraryUpdateNotifier(private val context: Context) {
|
class LibraryUpdateNotifier(
|
||||||
|
private val context: Context,
|
||||||
|
|
||||||
|
private val securityPreferences: SecurityPreferences = Injekt.get(),
|
||||||
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
|
) {
|
||||||
|
|
||||||
private val preferences: SecurityPreferences by injectLazy()
|
|
||||||
private val percentFormatter = NumberFormat.getPercentInstance().apply {
|
private val percentFormatter = NumberFormat.getPercentInstance().apply {
|
||||||
roundingMode = RoundingMode.DOWN
|
roundingMode = RoundingMode.DOWN
|
||||||
maximumFractionDigits = 0
|
maximumFractionDigits = 0
|
||||||
|
@ -88,7 +96,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!preferences.hideNotificationContent().get()) {
|
if (!securityPreferences.hideNotificationContent().get()) {
|
||||||
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
||||||
progressNotificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
progressNotificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
||||||
}
|
}
|
||||||
|
@ -101,7 +109,19 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showQueueSizeWarningNotification() {
|
/**
|
||||||
|
* Warn when excessively checking any single source.
|
||||||
|
*/
|
||||||
|
fun showQueueSizeWarningNotificationIfNeeded(mangaToUpdate: List<LibraryManga>) {
|
||||||
|
val maxUpdatesFromSource = mangaToUpdate
|
||||||
|
.groupBy { it.manga.source }
|
||||||
|
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
||||||
|
.maxOfOrNull { it.value.size } ?: 0
|
||||||
|
|
||||||
|
if (maxUpdatesFromSource <= MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
context.notify(
|
context.notify(
|
||||||
Notifications.ID_LIBRARY_SIZE_WARNING,
|
Notifications.ID_LIBRARY_SIZE_WARNING,
|
||||||
Notifications.CHANNEL_LIBRARY_PROGRESS,
|
Notifications.CHANNEL_LIBRARY_PROGRESS,
|
||||||
|
@ -151,7 +171,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
Notifications.CHANNEL_NEW_CHAPTERS,
|
Notifications.CHANNEL_NEW_CHAPTERS,
|
||||||
) {
|
) {
|
||||||
setContentTitle(context.stringResource(MR.strings.notification_new_chapters))
|
setContentTitle(context.stringResource(MR.strings.notification_new_chapters))
|
||||||
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
if (updates.size == 1 && !securityPreferences.hideNotificationContent().get()) {
|
||||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||||
} else {
|
} else {
|
||||||
setContentText(
|
setContentText(
|
||||||
|
@ -162,7 +182,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!preferences.hideNotificationContent().get()) {
|
if (!securityPreferences.hideNotificationContent().get()) {
|
||||||
setStyle(
|
setStyle(
|
||||||
NotificationCompat.BigTextStyle().bigText(
|
NotificationCompat.BigTextStyle().bigText(
|
||||||
updates.joinToString("\n") {
|
updates.joinToString("\n") {
|
||||||
|
@ -186,7 +206,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-manga notification
|
// Per-manga notification
|
||||||
if (!preferences.hideNotificationContent().get()) {
|
if (!securityPreferences.hideNotificationContent().get()) {
|
||||||
launchUI {
|
launchUI {
|
||||||
context.notify(
|
context.notify(
|
||||||
updates.map { (manga, chapters) ->
|
updates.map { (manga, chapters) ->
|
||||||
|
@ -364,3 +384,4 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
private const val NOTIF_MAX_CHAPTERS = 5
|
private const val NOTIF_MAX_CHAPTERS = 5
|
||||||
private const val NOTIF_TITLE_MAX_LEN = 45
|
private const val NOTIF_TITLE_MAX_LEN = 45
|
||||||
private const val NOTIF_ICON_SIZE = 192
|
private const val NOTIF_ICON_SIZE = 192
|
||||||
|
private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
|
||||||
|
|
|
@ -15,7 +15,6 @@ import eu.kanade.domain.manga.model.copyFrom
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
|
||||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||||
import eu.kanade.tachiyomi.util.system.isRunning
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
|
@ -25,7 +24,6 @@ import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
@ -92,17 +90,9 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
|
||||||
/**
|
/**
|
||||||
* Adds list of manga to be updated.
|
* Adds list of manga to be updated.
|
||||||
*/
|
*/
|
||||||
private fun addMangaToQueue() {
|
private suspend fun addMangaToQueue() {
|
||||||
mangaToUpdate = runBlocking { getLibraryManga.await() }
|
mangaToUpdate = getLibraryManga.await()
|
||||||
|
notifier.showQueueSizeWarningNotificationIfNeeded(mangaToUpdate)
|
||||||
// Warn when excessively checking a single source
|
|
||||||
val maxUpdatesFromSource = mangaToUpdate
|
|
||||||
.groupBy { it.manga.source }
|
|
||||||
.filterKeys { sourceManager.get(it) !is UnmeteredSource }
|
|
||||||
.maxOfOrNull { it.value.size } ?: 0
|
|
||||||
if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
|
|
||||||
notifier.showQueueSizeWarningNotification()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateMetadata() {
|
private suspend fun updateMetadata() {
|
||||||
|
|
|
@ -104,8 +104,6 @@ class MangaScreen(
|
||||||
MangaScreen(
|
MangaScreen(
|
||||||
state = successState,
|
state = successState,
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
dateRelativeTime = screenModel.relativeTime,
|
|
||||||
dateFormat = screenModel.dateFormat,
|
|
||||||
fetchInterval = successState.manga.fetchInterval,
|
fetchInterval = successState.manga.fetchInterval,
|
||||||
isTabletUi = isTabletUi(),
|
isTabletUi = isTabletUi(),
|
||||||
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
|
@ -22,7 +21,6 @@ import eu.kanade.domain.manga.model.chaptersFiltered
|
||||||
import eu.kanade.domain.manga.model.downloadedFilter
|
import eu.kanade.domain.manga.model.downloadedFilter
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.domain.track.interactor.AddTracks
|
import eu.kanade.domain.track.interactor.AddTracks
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.util.formattedMessage
|
import eu.kanade.presentation.util.formattedMessage
|
||||||
|
@ -92,7 +90,6 @@ class MangaScreenModel(
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
readerPreferences: ReaderPreferences = Injekt.get(),
|
readerPreferences: ReaderPreferences = Injekt.get(),
|
||||||
uiPreferences: UiPreferences = Injekt.get(),
|
|
||||||
private val trackerManager: TrackerManager = Injekt.get(),
|
private val trackerManager: TrackerManager = Injekt.get(),
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val downloadCache: DownloadCache = Injekt.get(),
|
private val downloadCache: DownloadCache = Injekt.get(),
|
||||||
|
@ -138,8 +135,6 @@ class MangaScreenModel(
|
||||||
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
|
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
|
||||||
val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get()
|
val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get()
|
||||||
|
|
||||||
val relativeTime by uiPreferences.relativeTime().asState(screenModelScope)
|
|
||||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
|
||||||
private val skipFiltered by readerPreferences.skipFiltered().asState(screenModelScope)
|
private val skipFiltered by readerPreferences.skipFiltered().asState(screenModelScope)
|
||||||
|
|
||||||
val isUpdateIntervalEnabled =
|
val isUpdateIntervalEnabled =
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
package eu.kanade.tachiyomi.ui.updates
|
package eu.kanade.tachiyomi.ui.updates
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import eu.kanade.core.preference.asState
|
import eu.kanade.core.preference.asState
|
||||||
import eu.kanade.core.util.addOrRemove
|
import eu.kanade.core.util.addOrRemove
|
||||||
import eu.kanade.core.util.insertSeparators
|
import eu.kanade.core.util.insertSeparators
|
||||||
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.updates.UpdatesUiModel
|
import eu.kanade.presentation.updates.UpdatesUiModel
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||||
|
@ -20,7 +17,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
import eu.kanade.tachiyomi.util.lang.toDateKey
|
import eu.kanade.tachiyomi.util.lang.toDateKey
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.mutate
|
import kotlinx.collections.immutable.mutate
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
@ -63,14 +59,12 @@ class UpdatesScreenModel(
|
||||||
private val getChapter: GetChapter = Injekt.get(),
|
private val getChapter: GetChapter = Injekt.get(),
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||||
uiPreferences: UiPreferences = Injekt.get(),
|
|
||||||
) : StateScreenModel<UpdatesScreenModel.State>(State()) {
|
) : StateScreenModel<UpdatesScreenModel.State>(State()) {
|
||||||
|
|
||||||
private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
|
private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
|
||||||
val events: Flow<Event> = _events.receiveAsFlow()
|
val events: Flow<Event> = _events.receiveAsFlow()
|
||||||
|
|
||||||
val lastUpdated by libraryPreferences.lastUpdatedTimestamp().asState(screenModelScope)
|
val lastUpdated by libraryPreferences.lastUpdatedTimestamp().asState(screenModelScope)
|
||||||
val relativeTime by uiPreferences.relativeTime().asState(screenModelScope)
|
|
||||||
|
|
||||||
// First and last selected index in list
|
// First and last selected index in list
|
||||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1)
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1)
|
||||||
|
@ -376,9 +370,7 @@ class UpdatesScreenModel(
|
||||||
val selected = items.filter { it.selected }
|
val selected = items.filter { it.selected }
|
||||||
val selectionMode = selected.isNotEmpty()
|
val selectionMode = selected.isNotEmpty()
|
||||||
|
|
||||||
fun getUiModel(context: Context, relativeTime: Boolean): List<UpdatesUiModel> {
|
fun getUiModel(): List<UpdatesUiModel> {
|
||||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
|
|
||||||
|
|
||||||
return items
|
return items
|
||||||
.map { UpdatesUiModel.Item(it) }
|
.map { UpdatesUiModel.Item(it) }
|
||||||
.insertSeparators { before, after ->
|
.insertSeparators { before, after ->
|
||||||
|
@ -386,12 +378,7 @@ class UpdatesScreenModel(
|
||||||
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
|
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
|
||||||
when {
|
when {
|
||||||
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
|
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
|
||||||
val text = afterDate.toRelativeString(
|
UpdatesUiModel.Header(afterDate)
|
||||||
context = context,
|
|
||||||
relative = relativeTime,
|
|
||||||
dateFormat = dateFormat,
|
|
||||||
)
|
|
||||||
UpdatesUiModel.Header(text)
|
|
||||||
}
|
}
|
||||||
// Return null to avoid adding a separator between two items.
|
// Return null to avoid adding a separator between two items.
|
||||||
else -> null
|
else -> null
|
||||||
|
|
|
@ -59,7 +59,6 @@ object UpdatesTab : Tab {
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
lastUpdated = screenModel.lastUpdated,
|
lastUpdated = screenModel.lastUpdated,
|
||||||
relativeTime = screenModel.relativeTime,
|
|
||||||
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },
|
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },
|
||||||
onSelectAll = screenModel::toggleAllSelection,
|
onSelectAll = screenModel::toggleAllSelection,
|
||||||
onInvertSelection = screenModel::invertSelection,
|
onInvertSelection = screenModel::invertSelection,
|
||||||
|
|
Reference in a new issue