mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2026-01-06 03:42:33 +01:00
Cleanup uneeded StableHolder classes
This commit is contained in:
@@ -39,7 +39,6 @@ import ca.gosyer.jui.domain.download.model.DownloadChapter
|
||||
import ca.gosyer.jui.domain.download.model.DownloadState
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.components.DropdownIconButton
|
||||
import ca.gosyer.jui.uicore.components.DropdownMenuItem
|
||||
import ca.gosyer.jui.uicore.resources.stringResource
|
||||
@@ -57,7 +56,7 @@ data class ChapterDownloadItem(
|
||||
ChapterDownloadState.NotDownloaded
|
||||
}
|
||||
),
|
||||
private val _downloadChapterFlow: MutableStateFlow<StableHolder<DownloadChapter?>> = MutableStateFlow(StableHolder(null))
|
||||
private val _downloadChapterFlow: MutableStateFlow<DownloadChapter?> = MutableStateFlow(null)
|
||||
) {
|
||||
val downloadState = _downloadState.asStateFlow()
|
||||
val downloadChapterFlow = _downloadChapterFlow.asStateFlow()
|
||||
@@ -72,7 +71,7 @@ data class ChapterDownloadItem(
|
||||
if (downloadState.value == ChapterDownloadState.Downloading && downloadingChapter == null) {
|
||||
_downloadState.value = ChapterDownloadState.Downloaded
|
||||
}
|
||||
_downloadChapterFlow.value = StableHolder(downloadingChapter)
|
||||
_downloadChapterFlow.value = downloadingChapter
|
||||
}
|
||||
|
||||
suspend fun deleteDownload(deleteChapterDownload: DeleteChapterDownload) {
|
||||
@@ -144,8 +143,7 @@ private fun DownloadIconButton(onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DownloadingIconButton(downloadChapterHolder: StableHolder<DownloadChapter?>, onClick: () -> Unit) {
|
||||
val downloadChapter = downloadChapterHolder.item
|
||||
private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: () -> Unit) {
|
||||
DropdownIconButton(
|
||||
downloadChapter?.mangaId to downloadChapter?.chapterIndex,
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ import ca.gosyer.jui.domain.download.interactor.ClearDownloadQueue
|
||||
import ca.gosyer.jui.domain.download.interactor.StartDownloading
|
||||
import ca.gosyer.jui.domain.download.interactor.StopDownloading
|
||||
import ca.gosyer.jui.domain.download.service.DownloadService
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -51,7 +50,7 @@ class DownloadsScreenViewModel @Inject constructor(
|
||||
|
||||
val serviceStatus get() = DownloadService.status.asStateFlow()
|
||||
val downloaderStatus get() = DownloadService.downloaderStatus.asStateFlow()
|
||||
val downloadQueue get() = DownloadService.downloadQueue.map { it.map(::StableHolder).toImmutableList() }
|
||||
val downloadQueue get() = DownloadService.downloadQueue.map { it.toImmutableList() }
|
||||
.stateIn(scope, SharingStarted.Eagerly, persistentListOf())
|
||||
|
||||
fun start() {
|
||||
|
||||
@@ -49,7 +49,6 @@ import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.download.model.DownloadChapter
|
||||
import ca.gosyer.jui.domain.download.model.DownloaderStatus
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.ActionItem
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
@@ -72,7 +71,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun DownloadsScreenContent(
|
||||
downloadQueue: ImmutableList<StableHolder<DownloadChapter>>,
|
||||
downloadQueue: ImmutableList<DownloadChapter>,
|
||||
downloadStatus: DownloaderStatus,
|
||||
startDownloading: () -> Unit,
|
||||
pauseDownloading: () -> Unit,
|
||||
@@ -115,7 +114,7 @@ fun DownloadsScreenContent(
|
||||
items(downloadQueue) {
|
||||
DownloadsItem(
|
||||
it,
|
||||
{ onMangaClick(it.item.mangaId) },
|
||||
{ onMangaClick(it.mangaId) },
|
||||
stopDownload,
|
||||
moveDownloadToBottom
|
||||
)
|
||||
@@ -140,12 +139,11 @@ fun DownloadsScreenContent(
|
||||
|
||||
@Composable
|
||||
fun DownloadsItem(
|
||||
itemHolder: StableHolder<DownloadChapter>,
|
||||
item: DownloadChapter,
|
||||
onClickCover: () -> Unit,
|
||||
onClickCancel: (Chapter) -> Unit,
|
||||
onClickMoveToBottom: (Chapter) -> Unit
|
||||
) {
|
||||
val item = itemHolder.item
|
||||
MangaListItem(
|
||||
modifier = Modifier
|
||||
.height(96.dp)
|
||||
|
||||
@@ -23,7 +23,6 @@ import ca.gosyer.jui.domain.manga.model.MangaStatus
|
||||
import ca.gosyer.jui.domain.updates.interactor.UpdateCategory
|
||||
import ca.gosyer.jui.domain.updates.interactor.UpdateLibrary
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.state.SavedStateHandle
|
||||
import ca.gosyer.jui.ui.base.state.getStateFlow
|
||||
import ca.gosyer.jui.ui.util.lang.Collator
|
||||
@@ -65,13 +64,13 @@ sealed class CategoryState {
|
||||
|
||||
@Stable
|
||||
data class Loaded(
|
||||
val items: StateFlow<ImmutableList<StableHolder<Manga>>>,
|
||||
val unfilteredItems: MutableStateFlow<ImmutableList<StableHolder<Manga>>>
|
||||
val items: StateFlow<ImmutableList<Manga>>,
|
||||
val unfilteredItems: MutableStateFlow<ImmutableList<Manga>>
|
||||
) : CategoryState()
|
||||
}
|
||||
|
||||
private typealias LibraryMap = MutableMap<Long, MutableStateFlow<CategoryState>>
|
||||
private data class Library(val categories: MutableStateFlow<ImmutableList<StableHolder<Category>>>, val mangaMap: LibraryMap)
|
||||
private data class Library(val categories: MutableStateFlow<ImmutableList<Category>>, val mangaMap: LibraryMap)
|
||||
|
||||
private fun LibraryMap.getManga(id: Long) =
|
||||
getOrPut(id) {
|
||||
@@ -80,7 +79,7 @@ private fun LibraryMap.getManga(id: Long) =
|
||||
private fun LibraryMap.setError(id: Long, e: Throwable) {
|
||||
getManga(id).value = CategoryState.Failed(e)
|
||||
}
|
||||
private fun LibraryMap.setManga(id: Long, manga: ImmutableList<StableHolder<Manga>>, getItemsFlow: (StateFlow<List<StableHolder<Manga>>>) -> StateFlow<ImmutableList<StableHolder<Manga>>>) {
|
||||
private fun LibraryMap.setManga(id: Long, manga: ImmutableList<Manga>, getItemsFlow: (StateFlow<List<Manga>>) -> StateFlow<ImmutableList<Manga>>) {
|
||||
val flow = getManga(id)
|
||||
when (val state = flow.value) {
|
||||
is CategoryState.Loaded -> state.unfilteredItems.value = manga
|
||||
@@ -121,12 +120,12 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
private val sortMode = libraryPreferences.sortMode().stateIn(scope)
|
||||
private val sortAscending = libraryPreferences.sortAscending().stateIn(scope)
|
||||
|
||||
private val filter: Flow<(StableHolder<Manga>) -> Boolean> = combine(
|
||||
private val filter: Flow<(Manga) -> Boolean> = combine(
|
||||
libraryPreferences.filterDownloaded().getAsFlow(),
|
||||
libraryPreferences.filterUnread().getAsFlow(),
|
||||
libraryPreferences.filterCompleted().getAsFlow()
|
||||
) { downloaded, unread, completed ->
|
||||
{ (manga) ->
|
||||
{ manga ->
|
||||
when (downloaded) {
|
||||
FilterState.EXCLUDED -> manga.downloadCount == null || manga.downloadCount == 0
|
||||
FilterState.INCLUDED -> manga.downloadCount != null && (manga.downloadCount ?: 0) > 0
|
||||
@@ -154,7 +153,7 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
|
||||
private val comparator = combine(sortMode, sortAscending) { sortMode, sortAscending ->
|
||||
getComparator(sortMode, sortAscending)
|
||||
}.stateIn(scope, SharingStarted.Eagerly, compareBy { it.item.title })
|
||||
}.stateIn(scope, SharingStarted.Eagerly, compareBy { it.title })
|
||||
|
||||
init {
|
||||
getLibrary()
|
||||
@@ -168,7 +167,6 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
throw Exception(MR.strings.library_empty.toPlatformString())
|
||||
}
|
||||
library.categories.value = categories.sortedBy { it.order }
|
||||
.map(::StableHolder)
|
||||
.toImmutableList()
|
||||
updateCategories(categories)
|
||||
_isLoading.value = false
|
||||
@@ -189,18 +187,18 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
_showingMenu.value = showingMenu
|
||||
}
|
||||
|
||||
private fun getComparator(sortMode: Sort, ascending: Boolean): Comparator<StableHolder<Manga>> {
|
||||
val sortFn: (StableHolder<Manga>, StableHolder<Manga>) -> Int = when (sortMode) {
|
||||
private fun getComparator(sortMode: Sort, ascending: Boolean): Comparator<Manga> {
|
||||
val sortFn: (Manga, Manga) -> Int = when (sortMode) {
|
||||
Sort.ALPHABETICAL -> {
|
||||
val locale = Locale.current
|
||||
val collator = Collator(locale);
|
||||
|
||||
{ (a), (b) ->
|
||||
{ a, b ->
|
||||
collator.compare(a.title.toLowerCase(locale), b.title.toLowerCase(locale))
|
||||
}
|
||||
}
|
||||
Sort.UNREAD -> {
|
||||
{ (a), (b) ->
|
||||
{ a, b ->
|
||||
when {
|
||||
// Ensure unread content comes first
|
||||
(a.unreadCount ?: 0) == (b.unreadCount ?: 0) -> 0
|
||||
@@ -211,7 +209,7 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
Sort.DATE_ADDED -> {
|
||||
{ (a), (b) ->
|
||||
{ a, b ->
|
||||
a.inLibraryAt.compareTo(b.inLibraryAt)
|
||||
}
|
||||
}
|
||||
@@ -223,11 +221,11 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun filterManga(query: String, mangaList: List<StableHolder<Manga>>): List<StableHolder<Manga>> {
|
||||
private suspend fun filterManga(query: String, mangaList: List<Manga>): List<Manga> {
|
||||
if (query.isBlank()) return mangaList
|
||||
val queries = query.split(" ")
|
||||
return mangaList.asFlow()
|
||||
.filter { (manga) ->
|
||||
.filter { manga ->
|
||||
queries.all { query ->
|
||||
manga.title.contains(query, true) ||
|
||||
manga.author.orEmpty().contains(query, true) ||
|
||||
@@ -242,7 +240,7 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun getMangaItemsFlow(unfilteredItemsFlow: StateFlow<List<StableHolder<Manga>>>): StateFlow<ImmutableList<StableHolder<Manga>>> {
|
||||
private fun getMangaItemsFlow(unfilteredItemsFlow: StateFlow<List<Manga>>): StateFlow<ImmutableList<Manga>> {
|
||||
return combine(unfilteredItemsFlow, query) { unfilteredItems, query ->
|
||||
filterManga(query, unfilteredItems)
|
||||
}.combine(filter) { filteredManga, filterer ->
|
||||
@@ -266,7 +264,7 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
.onEach {
|
||||
library.mangaMap.setManga(
|
||||
id = category.id,
|
||||
manga = it.map(::StableHolder).toImmutableList(),
|
||||
manga = it.toImmutableList(),
|
||||
getItemsFlow = ::getMangaItemsFlow
|
||||
)
|
||||
}
|
||||
@@ -280,12 +278,12 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCategoriesToUpdate(mangaId: Long): List<StableHolder<Category>> {
|
||||
private fun getCategoriesToUpdate(mangaId: Long): List<Category> {
|
||||
return library.mangaMap
|
||||
.filter { mangaMapEntry ->
|
||||
(mangaMapEntry.value.value as? CategoryState.Loaded)?.items?.value?.firstOrNull { it.item.id == mangaId } != null
|
||||
(mangaMapEntry.value.value as? CategoryState.Loaded)?.items?.value?.firstOrNull { it.id == mangaId } != null
|
||||
}
|
||||
.map { (id) -> library.categories.value.first { it.item.id == id } }
|
||||
.map { (id) -> library.categories.value.first { it.id == id } }
|
||||
}
|
||||
|
||||
fun removeManga(mangaId: Long) {
|
||||
|
||||
@@ -23,19 +23,17 @@ import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.resources.stringResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaBadges(
|
||||
modifier: Modifier = Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
showUnread: Boolean,
|
||||
showDownloaded: Boolean,
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val unread = manga.unreadCount
|
||||
val downloaded = manga.downloadCount
|
||||
val isLocal = manga.sourceId == Source.LOCAL_SOURCE_ID
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.MangaListItem
|
||||
import ca.gosyer.jui.uicore.components.MangaListItemImage
|
||||
@@ -43,7 +42,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaList(
|
||||
library: ImmutableList<StableHolder<Manga>>,
|
||||
library: ImmutableList<Manga>,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit,
|
||||
showUnread: Boolean,
|
||||
@@ -62,13 +61,13 @@ fun LibraryMangaList(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
items(library) { mangaHolder ->
|
||||
items(library) { manga ->
|
||||
LibraryMangaListItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(mangaHolder.item.id) },
|
||||
{ onRemoveMangaClicked(mangaHolder.item.id) }
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
@@ -95,13 +94,12 @@ fun LibraryMangaList(
|
||||
@Composable
|
||||
private fun LibraryMangaListItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
showUnread: Boolean,
|
||||
showDownloaded: Boolean,
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
@@ -122,7 +120,7 @@ private fun LibraryMangaListItem(
|
||||
)
|
||||
Box(Modifier.width(IntrinsicSize.Min)) {
|
||||
LibraryMangaBadges(
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
|
||||
@@ -13,7 +13,6 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.library.model.DisplayMode
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.library.CategoryState
|
||||
import ca.gosyer.jui.uicore.components.ErrorScreen
|
||||
import ca.gosyer.jui.uicore.components.LoadingScreen
|
||||
@@ -24,7 +23,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
@Composable
|
||||
fun LibraryPager(
|
||||
pagerState: PagerState,
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
@@ -39,7 +38,7 @@ fun LibraryPager(
|
||||
if (categories.isEmpty()) return
|
||||
|
||||
HorizontalPager(categories.size, state = pagerState) {
|
||||
when (val library = getLibraryForPage(categories[it].item.id).value) {
|
||||
when (val library = getLibraryForPage(categories[it].id).value) {
|
||||
CategoryState.Loading -> LoadingScreen()
|
||||
is CategoryState.Failed -> ErrorScreen(library.e.message)
|
||||
is CategoryState.Loaded -> LibraryLoadedPage(
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.library.model.DisplayMode
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.ActionItem
|
||||
import ca.gosyer.jui.ui.base.navigation.BackHandler
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
@@ -58,7 +57,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun LibraryScreenContent(
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
@@ -156,7 +155,7 @@ fun LibraryScreenContent(
|
||||
@Composable
|
||||
fun WideLibraryScreenContent(
|
||||
pagerState: PagerState,
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
@@ -257,7 +256,7 @@ fun WideLibraryScreenContent(
|
||||
@Composable
|
||||
fun ThinLibraryScreenContent(
|
||||
pagerState: PagerState,
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import com.google.accompanist.pager.PagerState
|
||||
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -28,7 +27,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
fun LibraryTabs(
|
||||
visible: Boolean,
|
||||
pagerState: PagerState,
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
selectedPage: Int,
|
||||
onPageChanged: (Int) -> Unit
|
||||
) {
|
||||
@@ -50,7 +49,7 @@ fun LibraryTabs(
|
||||
)
|
||||
}
|
||||
) {
|
||||
categories.fastForEachIndexed { i, (category) ->
|
||||
categories.fastForEachIndexed { i, category ->
|
||||
Tab(
|
||||
selected = selectedPage == i,
|
||||
onClick = { onPageChanged(i) },
|
||||
|
||||
@@ -37,7 +37,6 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
@@ -49,7 +48,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaComfortableGrid(
|
||||
library: ImmutableList<StableHolder<Manga>>,
|
||||
library: ImmutableList<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -76,13 +75,13 @@ fun LibraryMangaComfortableGrid(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
items(library) { mangaHolder ->
|
||||
items(library) { manga ->
|
||||
LibraryMangaComfortableGridItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(mangaHolder.item.id) },
|
||||
{ onRemoveMangaClicked(mangaHolder.item.id) }
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
@@ -109,13 +108,12 @@ fun LibraryMangaComfortableGrid(
|
||||
@Composable
|
||||
private fun LibraryMangaComfortableGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
showUnread: Boolean,
|
||||
showDownloaded: Boolean,
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -146,7 +144,7 @@ private fun LibraryMangaComfortableGridItem(
|
||||
}
|
||||
LibraryMangaBadges(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
@@ -57,7 +56,7 @@ expect fun Modifier.libraryMangaModifier(
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaCompactGrid(
|
||||
library: ImmutableList<StableHolder<Manga>>,
|
||||
library: ImmutableList<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -84,13 +83,13 @@ fun LibraryMangaCompactGrid(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
items(library) { mangaHolder ->
|
||||
items(library) { manga ->
|
||||
LibraryMangaCompactGridItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(mangaHolder.item.id) },
|
||||
{ onRemoveMangaClicked(mangaHolder.item.id) }
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
@@ -117,13 +116,12 @@ fun LibraryMangaCompactGrid(
|
||||
@Composable
|
||||
private fun LibraryMangaCompactGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
showUnread: Boolean,
|
||||
showDownloaded: Boolean,
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -151,7 +149,7 @@ private fun LibraryMangaCompactGridItem(
|
||||
)
|
||||
LibraryMangaBadges(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
@@ -43,7 +42,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaCoverOnlyGrid(
|
||||
library: ImmutableList<StableHolder<Manga>>,
|
||||
library: ImmutableList<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -70,13 +69,13 @@ fun LibraryMangaCoverOnlyGrid(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
items(library) { mangaHolder ->
|
||||
items(library) { manga ->
|
||||
LibraryMangaCoverOnlyGridItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(mangaHolder.item.id) },
|
||||
{ onRemoveMangaClicked(mangaHolder.item.id) }
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
@@ -103,13 +102,12 @@ fun LibraryMangaCoverOnlyGrid(
|
||||
@Composable
|
||||
private fun LibraryMangaCoverOnlyGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
showUnread: Boolean,
|
||||
showDownloaded: Boolean,
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
Box(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
@@ -125,7 +123,7 @@ private fun LibraryMangaCoverOnlyGridItem(
|
||||
)
|
||||
LibraryMangaBadges(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
mangaHolder = mangaHolder,
|
||||
manga = manga,
|
||||
showUnread = showUnread,
|
||||
showDownloaded = showDownloaded,
|
||||
showLanguage = showLanguage,
|
||||
|
||||
@@ -37,7 +37,7 @@ class AboutScreen : Screen {
|
||||
}
|
||||
}
|
||||
AboutContent(
|
||||
aboutHolder = vm.aboutHolder.collectAsState().value,
|
||||
about = vm.aboutHolder.collectAsState().value,
|
||||
formattedBuildTime = vm.formattedBuildTime.collectAsState().value,
|
||||
checkForUpdates = vm::checkForUpdates,
|
||||
openSourceLicenses = {
|
||||
|
||||
@@ -11,7 +11,6 @@ import ca.gosyer.jui.domain.settings.interactor.AboutServer
|
||||
import ca.gosyer.jui.domain.settings.model.About
|
||||
import ca.gosyer.jui.domain.updates.interactor.UpdateChecker
|
||||
import ca.gosyer.jui.domain.updates.interactor.UpdateChecker.Update
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
@@ -33,10 +32,10 @@ class AboutViewModel @Inject constructor(
|
||||
contextWrapper: ContextWrapper
|
||||
) : ViewModel(contextWrapper) {
|
||||
|
||||
private val _aboutHolder = MutableStateFlow<StableHolder<About?>>(StableHolder(null))
|
||||
private val _aboutHolder = MutableStateFlow<About?>(null)
|
||||
val aboutHolder = _aboutHolder.asStateFlow()
|
||||
|
||||
val formattedBuildTime = aboutHolder.map { (about) ->
|
||||
val formattedBuildTime = aboutHolder.map { about ->
|
||||
about ?: return@map ""
|
||||
getFormattedDate(Instant.fromEpochSeconds(about.buildTime))
|
||||
}.stateIn(scope, SharingStarted.Eagerly, "")
|
||||
@@ -50,7 +49,7 @@ class AboutViewModel @Inject constructor(
|
||||
|
||||
private fun getAbout() {
|
||||
scope.launch {
|
||||
_aboutHolder.value = StableHolder(aboutServer.await())
|
||||
_aboutHolder.value = aboutServer.await()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ import ca.gosyer.jui.domain.settings.model.About
|
||||
import ca.gosyer.jui.domain.updates.interactor.UpdateChecker
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.presentation.build.BuildKonfig
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.jui.ui.base.prefs.PreferenceRow
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
@@ -61,7 +60,7 @@ import dev.icerock.moko.resources.StringResource
|
||||
|
||||
@Composable
|
||||
fun AboutContent(
|
||||
aboutHolder: StableHolder<About?>,
|
||||
about: About?,
|
||||
formattedBuildTime: String,
|
||||
checkForUpdates: () -> Unit,
|
||||
openSourceLicenses: () -> Unit
|
||||
@@ -100,7 +99,7 @@ fun AboutContent(
|
||||
ClientVersionInfo()
|
||||
}
|
||||
item {
|
||||
ServerVersionInfo(aboutHolder, formattedBuildTime)
|
||||
ServerVersionInfo(about, formattedBuildTime)
|
||||
}
|
||||
item {
|
||||
WhatsNew()
|
||||
@@ -174,8 +173,7 @@ private fun ClientVersionInfo() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ServerVersionInfo(aboutHolder: StableHolder<About?>, formattedBuildTime: String) {
|
||||
val about = aboutHolder.item
|
||||
private fun ServerVersionInfo(about: About?, formattedBuildTime: String) {
|
||||
if (about == null) {
|
||||
Box(Modifier.fillMaxWidth().height(48.dp), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator()
|
||||
|
||||
@@ -26,7 +26,7 @@ class MangaScreen(private val mangaId: Long) : Screen {
|
||||
|
||||
MangaScreenContent(
|
||||
isLoading = vm.isLoading.collectAsState().value,
|
||||
mangaHolder = vm.manga.collectAsState().value,
|
||||
manga = vm.manga.collectAsState().value,
|
||||
chapters = vm.chapters.collectAsState().value,
|
||||
dateTimeFormatter = vm.dateTimeFormatter.collectAsState().value,
|
||||
categoriesExist = vm.categoriesExist.collectAsState().value,
|
||||
|
||||
@@ -73,7 +73,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
contextWrapper: ContextWrapper,
|
||||
private val params: Params
|
||||
) : ViewModel(contextWrapper) {
|
||||
private val _manga = MutableStateFlow<StableHolder<Manga?>>(StableHolder(null))
|
||||
private val _manga = MutableStateFlow<Manga?>(null)
|
||||
val manga = _manga.asStateFlow()
|
||||
|
||||
private val _chapters = MutableStateFlow<ImmutableList<ChapterDownloadItem>>(persistentListOf())
|
||||
@@ -83,11 +83,11 @@ class MangaScreenViewModel @Inject constructor(
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
val categories = getCategories.asFlow(true)
|
||||
.map { it.map(::StableHolder).toImmutableList() }
|
||||
.map { it.toImmutableList() }
|
||||
.catch { log.warn(it) { "Failed to get categories" } }
|
||||
.stateIn(scope, SharingStarted.Eagerly, persistentListOf())
|
||||
|
||||
private val _mangaCategories = MutableStateFlow<ImmutableList<StableHolder<Category>>>(persistentListOf())
|
||||
private val _mangaCategories = MutableStateFlow<ImmutableList<Category>>(persistentListOf())
|
||||
val mangaCategories = _mangaCategories.asStateFlow()
|
||||
|
||||
val categoriesExist = categories.map { it.isNotEmpty() }
|
||||
@@ -143,7 +143,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
|
||||
fun setCategories() {
|
||||
scope.launch {
|
||||
manga.value.item ?: return@launch
|
||||
manga.value ?: return@launch
|
||||
chooseCategoriesFlow.emit(Unit)
|
||||
}
|
||||
}
|
||||
@@ -156,14 +156,14 @@ class MangaScreenViewModel @Inject constructor(
|
||||
getManga.await(mangaId)
|
||||
}
|
||||
if (manga != null) {
|
||||
_manga.value = StableHolder(manga)
|
||||
_manga.value = manga
|
||||
} else {
|
||||
// TODO: 2022-07-01 Error toast
|
||||
}
|
||||
|
||||
val mangaCategories = getMangaCategories.await(mangaId)
|
||||
if (mangaCategories != null) {
|
||||
_mangaCategories.value = mangaCategories.map(::StableHolder).toImmutableList()
|
||||
_mangaCategories.value = mangaCategories.toImmutableList()
|
||||
} else {
|
||||
// TODO: 2022-07-01 Error toast
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
|
||||
fun toggleFavorite() {
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
if (manga.inLibrary) {
|
||||
removeMangaFromLibrary.await(manga)
|
||||
refreshMangaAsync(manga.id).await()
|
||||
@@ -204,7 +204,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
|
||||
fun addFavorite(categories: List<Category>, oldCategories: List<Category>) {
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
if (manga.inLibrary) {
|
||||
oldCategories.filterNot { it in categories }.forEach {
|
||||
removeMangaFromCategory.await(manga, it)
|
||||
@@ -225,7 +225,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
fun toggleRead(index: Int) {
|
||||
val chapter = findChapter(index) ?: return
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
updateChapterRead.await(manga, index, read = chapter.read.not())
|
||||
refreshChaptersAsync(manga.id).await()
|
||||
}
|
||||
@@ -235,7 +235,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
fun toggleBookmarked(index: Int) {
|
||||
val chapter = findChapter(index) ?: return
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
updateChapterBookmarked.await(manga, index, bookmarked = chapter.bookmarked.not())
|
||||
refreshChaptersAsync(manga.id).await()
|
||||
}
|
||||
@@ -244,7 +244,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
|
||||
fun markPreviousRead(index: Int) {
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
updateChapterMarkPreviousRead.await(manga, index)
|
||||
refreshChaptersAsync(manga.id).await()
|
||||
}
|
||||
@@ -252,7 +252,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun downloadChapter(index: Int) {
|
||||
manga.value.item?.let { manga ->
|
||||
manga.value?.let { manga ->
|
||||
scope.launch { queueChapterDownload.await(manga, index) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.dialog.getMaterialDialogProperties
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||
@@ -52,30 +51,29 @@ import com.vanpra.composematerialdialogs.title
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun MangaItem(mangaHolder: StableHolder<Manga>) {
|
||||
fun MangaItem(manga: Manga) {
|
||||
BoxWithConstraints(Modifier.padding(8.dp)) {
|
||||
if (maxWidth > 720.dp) {
|
||||
Row {
|
||||
Cover(mangaHolder, Modifier.width(300.dp))
|
||||
Cover(manga, Modifier.width(300.dp))
|
||||
Spacer(Modifier.width(16.dp))
|
||||
MangaInfo(mangaHolder)
|
||||
MangaInfo(manga)
|
||||
}
|
||||
} else {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Cover(
|
||||
mangaHolder,
|
||||
manga,
|
||||
Modifier.heightIn(120.dp, 300.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
MangaInfo(mangaHolder)
|
||||
MangaInfo(manga)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Cover(mangaHolder: StableHolder<Manga>, modifier: Modifier = Modifier) {
|
||||
val manga = mangaHolder.item
|
||||
private fun Cover(manga: Manga, modifier: Modifier = Modifier) {
|
||||
ImageLoaderImage(
|
||||
data = manga,
|
||||
contentDescription = manga.title,
|
||||
@@ -90,8 +88,7 @@ private fun Cover(mangaHolder: StableHolder<Manga>, modifier: Modifier = Modifie
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MangaInfo(mangaHolder: StableHolder<Manga>, modifier: Modifier = Modifier) {
|
||||
val manga = mangaHolder.item
|
||||
private fun MangaInfo(manga: Manga, modifier: Modifier = Modifier) {
|
||||
SelectionContainer {
|
||||
Column(modifier) {
|
||||
Text(
|
||||
@@ -165,8 +162,8 @@ private fun Chip(text: String) {
|
||||
@Composable
|
||||
fun CategorySelectDialog(
|
||||
state: MaterialDialogState,
|
||||
categories: ImmutableList<StableHolder<Category>>,
|
||||
oldCategories: ImmutableList<StableHolder<Category>>,
|
||||
categories: ImmutableList<Category>,
|
||||
oldCategories: ImmutableList<Category>,
|
||||
onPositiveClick: (List<Category>, List<Category>) -> Unit
|
||||
) {
|
||||
MaterialDialog(
|
||||
@@ -182,13 +179,13 @@ fun CategorySelectDialog(
|
||||
Box {
|
||||
val listState = rememberLazyListState()
|
||||
listItemsMultiChoice(
|
||||
list = categories.map { it.item.name },
|
||||
list = categories.map { it.name },
|
||||
state = listState,
|
||||
initialSelection = oldCategories.mapNotNull { category ->
|
||||
categories.indexOfFirst { it.item.id == category.item.id }.takeUnless { it == -1 }
|
||||
categories.indexOfFirst { it.id == category.id }.takeUnless { it == -1 }
|
||||
}.toSet(),
|
||||
onCheckedChange = { indexes ->
|
||||
onPositiveClick(indexes.map { categories[it].item }, oldCategories.map { it.item })
|
||||
onPositiveClick(indexes.map { categories[it] }, oldCategories)
|
||||
}
|
||||
)
|
||||
VerticalScrollbar(
|
||||
|
||||
@@ -61,13 +61,13 @@ import kotlinx.datetime.Instant
|
||||
@Composable
|
||||
fun MangaScreenContent(
|
||||
isLoading: Boolean,
|
||||
mangaHolder: StableHolder<Manga?>,
|
||||
manga: Manga?,
|
||||
chapters: ImmutableList<ChapterDownloadItem>,
|
||||
dateTimeFormatter: (Instant) -> String,
|
||||
categoriesExist: Boolean,
|
||||
chooseCategoriesFlowHolder: StableHolder<SharedFlow<Unit>>,
|
||||
availableCategories: ImmutableList<StableHolder<Category>>,
|
||||
mangaCategories: ImmutableList<StableHolder<Category>>,
|
||||
availableCategories: ImmutableList<Category>,
|
||||
mangaCategories: ImmutableList<Category>,
|
||||
addFavorite: (List<Category>, List<Category>) -> Unit,
|
||||
setCategories: () -> Unit,
|
||||
toggleFavorite: () -> Unit,
|
||||
@@ -100,7 +100,6 @@ fun MangaScreenContent(
|
||||
Toolbar(
|
||||
stringResource(MR.strings.location_manga),
|
||||
actions = {
|
||||
val manga = mangaHolder.item
|
||||
val uriHandler = LocalUriHandler.current
|
||||
getActionItems(
|
||||
refreshManga = refreshManga,
|
||||
@@ -120,8 +119,8 @@ fun MangaScreenContent(
|
||||
}
|
||||
) {
|
||||
Box(Modifier.padding(it)) {
|
||||
mangaHolder.let { mangaHolder ->
|
||||
if (mangaHolder.item != null) {
|
||||
manga.let { manga ->
|
||||
if (manga != null) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(
|
||||
@@ -135,14 +134,14 @@ fun MangaScreenContent(
|
||||
) {
|
||||
item {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
MangaItem(mangaHolder as StableHolder<Manga>)
|
||||
MangaItem(manga)
|
||||
}
|
||||
if (chapters.isNotEmpty()) {
|
||||
items(chapters) { chapter ->
|
||||
ChapterItem(
|
||||
chapter,
|
||||
dateTimeFormatter,
|
||||
onClick = { readerLauncher.launch(it, mangaHolder.item.id) },
|
||||
onClick = { readerLauncher.launch(it, manga.id) },
|
||||
toggleRead = toggleRead,
|
||||
toggleBookmarked = toggleBookmarked,
|
||||
markPreviousAsRead = markPreviousRead,
|
||||
|
||||
@@ -8,9 +8,7 @@ package ca.gosyer.jui.ui.sources.browse
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.manga.MangaScreen
|
||||
import ca.gosyer.jui.ui.sources.browse.components.SourceScreenContent
|
||||
import ca.gosyer.jui.ui.sources.browse.filter.SourceFiltersViewModel
|
||||
@@ -28,7 +26,6 @@ class SourceScreen(val source: Source, private val initialQuery: String? = null)
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val sourceHolder = remember { StableHolder(source) }
|
||||
val sourceVM = viewModel {
|
||||
sourceViewModel(SourceScreenViewModel.Params(source, initialQuery))
|
||||
}
|
||||
@@ -38,7 +35,7 @@ class SourceScreen(val source: Source, private val initialQuery: String? = null)
|
||||
val sourcesNavigator = LocalSourcesNavigator.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
SourceScreenContent(
|
||||
sourceHolder = sourceHolder,
|
||||
source = source,
|
||||
onMangaClick = { navigator push MangaScreen(it) },
|
||||
onCloseSourceTabClick = if (sourcesNavigator != null) {
|
||||
{ sourcesNavigator.remove(it) }
|
||||
|
||||
@@ -13,7 +13,6 @@ import ca.gosyer.jui.domain.source.model.MangaPage
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -58,7 +57,7 @@ class SourceScreenViewModel(
|
||||
val gridColumns = libraryPreferences.gridColumns().stateIn(scope)
|
||||
val gridSize = libraryPreferences.gridSize().stateIn(scope)
|
||||
|
||||
private val _mangas = MutableStateFlow<ImmutableList<StableHolder<Manga>>>(persistentListOf())
|
||||
private val _mangas = MutableStateFlow<ImmutableList<Manga>>(persistentListOf())
|
||||
val mangas = _mangas.asStateFlow()
|
||||
|
||||
private val _hasNextPage = MutableStateFlow(false)
|
||||
@@ -88,7 +87,7 @@ class SourceScreenViewModel(
|
||||
init {
|
||||
scope.launch {
|
||||
getPage()?.let { (mangas, hasNextPage) ->
|
||||
_mangas.value = mangas.map(::StableHolder).toImmutableList()
|
||||
_mangas.value = mangas.toImmutableList()
|
||||
_hasNextPage.value = hasNextPage
|
||||
}
|
||||
|
||||
@@ -102,7 +101,7 @@ class SourceScreenViewModel(
|
||||
_pageNum.value++
|
||||
val page = getPage()
|
||||
if (page != null) {
|
||||
_mangas.value = _mangas.value.toPersistentList() + page.mangaList.map(::StableHolder)
|
||||
_mangas.value = _mangas.value.toPersistentList() + page.mangaList
|
||||
_hasNextPage.value = page.hasNextPage
|
||||
} else {
|
||||
_pageNum.value--
|
||||
|
||||
@@ -39,7 +39,6 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
@@ -51,7 +50,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun SourceMangaComfortableGrid(
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -75,16 +74,16 @@ fun SourceMangaComfortableGrid(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
itemsIndexed(mangas) { index, mangaHolder ->
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
SourceMangaComfortableGridItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(mangaHolder.item.id) }
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
inLibrary = mangaHolder.item.inLibrary
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -107,10 +106,9 @@ fun SourceMangaComfortableGrid(
|
||||
@Composable
|
||||
private fun SourceMangaComfortableGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
@@ -42,7 +42,6 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
@@ -54,7 +53,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun SourceMangaCompactGrid(
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -78,16 +77,16 @@ fun SourceMangaCompactGrid(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
itemsIndexed(mangas) { index, mangaHolder ->
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
SourceMangaCompactGridItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(mangaHolder.item.id) }
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
inLibrary = mangaHolder.item.inLibrary
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -110,10 +109,9 @@ fun SourceMangaCompactGrid(
|
||||
@Composable
|
||||
private fun SourceMangaCompactGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.uicore.components.MangaListItem
|
||||
import ca.gosyer.jui.uicore.components.MangaListItemImage
|
||||
@@ -43,7 +42,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun SourceMangaList(
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
onClickManga: (Long) -> Unit,
|
||||
hasNextPage: Boolean = false,
|
||||
onLoadNextPage: () -> Unit
|
||||
@@ -59,16 +58,16 @@ fun SourceMangaList(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
itemsIndexed(mangas) { index, mangaHolder ->
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
MangaListItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(mangaHolder.item.id) }
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
mangaHolder = mangaHolder,
|
||||
inLibrary = mangaHolder.item.inLibrary
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -91,10 +90,9 @@ fun SourceMangaList(
|
||||
@Composable
|
||||
private fun MangaListItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
|
||||
@@ -52,7 +52,6 @@ import ca.gosyer.jui.domain.library.model.DisplayMode
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.ActionItem
|
||||
import ca.gosyer.jui.ui.base.navigation.BackHandler
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
@@ -70,14 +69,14 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun SourceScreenContent(
|
||||
sourceHolder: StableHolder<Source>,
|
||||
source: Source,
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
isLatest: Boolean,
|
||||
@@ -91,13 +90,12 @@ fun SourceScreenContent(
|
||||
setUsingFilters: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
// filter
|
||||
filters: ImmutableList<StableHolder<SourceFiltersView<*, *>>>,
|
||||
filters: ImmutableList<SourceFiltersView<*, *>>,
|
||||
showingFilters: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
setShowingFilters: (Boolean) -> Unit,
|
||||
resetFiltersClicked: () -> Unit
|
||||
) {
|
||||
val source = sourceHolder.item
|
||||
LaunchedEffect(source) {
|
||||
enableLatest(source.supportsLatest)
|
||||
}
|
||||
@@ -113,7 +111,7 @@ fun SourceScreenContent(
|
||||
BoxWithConstraints {
|
||||
if (maxWidth > 720.dp) {
|
||||
SourceWideScreenContent(
|
||||
sourceHolder = sourceHolder,
|
||||
source = source,
|
||||
onMangaClick = onMangaClick,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
onSourceSettingsClick = onSourceSettingsClick,
|
||||
@@ -140,7 +138,7 @@ fun SourceScreenContent(
|
||||
)
|
||||
} else {
|
||||
SourceThinScreenContent(
|
||||
sourceHolder = sourceHolder,
|
||||
source = source,
|
||||
onMangaClick = onMangaClick,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
onSourceSettingsClick = onSourceSettingsClick,
|
||||
@@ -171,14 +169,14 @@ fun SourceScreenContent(
|
||||
|
||||
@Composable
|
||||
private fun SourceWideScreenContent(
|
||||
sourceHolder: StableHolder<Source>,
|
||||
source: Source,
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
isLatest: Boolean,
|
||||
@@ -190,7 +188,7 @@ private fun SourceWideScreenContent(
|
||||
loadNextPage: () -> Unit,
|
||||
setUsingFilters: (Boolean) -> Unit,
|
||||
// filter
|
||||
filters: ImmutableList<StableHolder<SourceFiltersView<*, *>>>,
|
||||
filters: ImmutableList<SourceFiltersView<*, *>>,
|
||||
showingFilters: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
setShowingFilters: (Boolean) -> Unit,
|
||||
@@ -205,7 +203,7 @@ private fun SourceWideScreenContent(
|
||||
),
|
||||
topBar = {
|
||||
SourceToolbar(
|
||||
sourceHolder = sourceHolder,
|
||||
source = source,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
sourceSearchQuery = sourceSearchQuery,
|
||||
onSearch = search,
|
||||
@@ -266,14 +264,14 @@ private fun SourceWideScreenContent(
|
||||
|
||||
@Composable
|
||||
private fun SourceThinScreenContent(
|
||||
sourceHolder: StableHolder<Source>,
|
||||
source: Source,
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
isLatest: Boolean,
|
||||
@@ -285,7 +283,7 @@ private fun SourceThinScreenContent(
|
||||
loadNextPage: () -> Unit,
|
||||
setUsingFilters: (Boolean) -> Unit,
|
||||
// filter
|
||||
filters: ImmutableList<StableHolder<SourceFiltersView<*, *>>>,
|
||||
filters: ImmutableList<SourceFiltersView<*, *>>,
|
||||
showingFilters: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
setShowingFilters: (Boolean) -> Unit,
|
||||
@@ -318,7 +316,7 @@ private fun SourceThinScreenContent(
|
||||
),
|
||||
topBar = {
|
||||
SourceToolbar(
|
||||
sourceHolder = sourceHolder,
|
||||
source = source,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
sourceSearchQuery = sourceSearchQuery,
|
||||
onSearch = search,
|
||||
@@ -410,7 +408,7 @@ private fun SourceThinScreenContent(
|
||||
|
||||
@Composable
|
||||
fun SourceToolbar(
|
||||
sourceHolder: StableHolder<Source>,
|
||||
source: Source,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
sourceSearchQuery: String?,
|
||||
onSearch: (String) -> Unit,
|
||||
@@ -424,7 +422,6 @@ fun SourceToolbar(
|
||||
onToggleFiltersClick: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit
|
||||
) {
|
||||
val source = sourceHolder.item
|
||||
Toolbar(
|
||||
source.name,
|
||||
closable = true,
|
||||
@@ -490,7 +487,7 @@ private fun MangaTable(
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: ImmutableList<StableHolder<Manga>>,
|
||||
mangas: ImmutableList<Manga>,
|
||||
isLoading: Boolean = false,
|
||||
hasNextPage: Boolean = false,
|
||||
onLoadNextPage: () -> Unit,
|
||||
|
||||
@@ -60,7 +60,6 @@ import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SortFilter
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.prefs.ExpandablePreference
|
||||
import ca.gosyer.jui.ui.sources.browse.filter.model.SourceFiltersView
|
||||
import ca.gosyer.jui.uicore.components.Spinner
|
||||
@@ -75,7 +74,7 @@ import kotlinx.coroutines.flow.filterIsInstance
|
||||
@Composable
|
||||
fun SourceFiltersMenu(
|
||||
modifier: Modifier,
|
||||
filters: ImmutableList<StableHolder<SourceFiltersView<*, *>>>,
|
||||
filters: ImmutableList<SourceFiltersView<*, *>>,
|
||||
onSearchClicked: () -> Unit,
|
||||
resetFiltersClicked: () -> Unit
|
||||
) {
|
||||
@@ -100,7 +99,7 @@ fun SourceFiltersMenu(
|
||||
val scrollState = rememberScrollState()
|
||||
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
||||
filters.fastForEach { item ->
|
||||
item.toView(startExpanded = item.item.index in expandedGroups) { expanded, index ->
|
||||
item.toView(startExpanded = item.index in expandedGroups) { expanded, index ->
|
||||
if (expanded) {
|
||||
expandedGroups += index
|
||||
} else {
|
||||
@@ -120,32 +119,17 @@ fun SourceFiltersMenu(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Composable
|
||||
fun StableHolder<SourceFiltersView<*, *>>.toView(startExpanded: Boolean = false, onExpandChanged: ((Boolean, Int) -> Unit)? = null) {
|
||||
when (this.item) {
|
||||
is SourceFiltersView.CheckBox -> CheckboxView(this as StableHolder<SourceFiltersView.CheckBox>)
|
||||
is SourceFiltersView.Group -> GroupView(this as StableHolder<SourceFiltersView.Group>, startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Header -> HeaderView(this as StableHolder<SourceFiltersView.Header>)
|
||||
is SourceFiltersView.Select -> SelectView(this as StableHolder<SourceFiltersView.Select>)
|
||||
is SourceFiltersView.Separator -> SeparatorView()
|
||||
is SourceFiltersView.Sort -> SortView(this as StableHolder<SourceFiltersView.Sort>, startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Text -> TextView(this as StableHolder<SourceFiltersView.Text>)
|
||||
is SourceFiltersView.TriState -> TriStateView(this as StableHolder<SourceFiltersView.TriState>)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFiltersView<*, *>.toView(startExpanded: Boolean = false, onExpandChanged: ((Boolean, Int) -> Unit)? = null) {
|
||||
when (this) {
|
||||
is SourceFiltersView.CheckBox -> CheckboxView(StableHolder(this))
|
||||
is SourceFiltersView.Group -> GroupView(StableHolder(this), startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Header -> HeaderView(StableHolder(this))
|
||||
is SourceFiltersView.Select -> SelectView(StableHolder(this))
|
||||
is SourceFiltersView.CheckBox -> CheckboxView(this)
|
||||
is SourceFiltersView.Group -> GroupView(this, startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Header -> HeaderView(this)
|
||||
is SourceFiltersView.Select -> SelectView(this)
|
||||
is SourceFiltersView.Separator -> SeparatorView()
|
||||
is SourceFiltersView.Sort -> SortView(StableHolder(this), startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Text -> TextView(StableHolder(this))
|
||||
is SourceFiltersView.TriState -> TriStateView(StableHolder(this))
|
||||
is SourceFiltersView.Sort -> SortView(this, startExpanded, onExpandChanged)
|
||||
is SourceFiltersView.Text -> TextView(this)
|
||||
is SourceFiltersView.TriState -> TriStateView(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +159,7 @@ fun SourceFilterAction(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GroupView(groupHolder: StableHolder<SourceFiltersView.Group>, startExpanded: Boolean, onExpandChanged: ((Boolean, Int) -> Unit)? = null) {
|
||||
val group = groupHolder.item
|
||||
fun GroupView(group: SourceFiltersView.Group, startExpanded: Boolean, onExpandChanged: ((Boolean, Int) -> Unit)? = null) {
|
||||
val state by key(group.hashCode()) { group.state.collectAsState() }
|
||||
ExpandablePreference(
|
||||
title = group.name,
|
||||
@@ -192,8 +175,7 @@ fun GroupView(groupHolder: StableHolder<SourceFiltersView.Group>, startExpanded:
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CheckboxView(checkBoxHolder: StableHolder<SourceFiltersView.CheckBox>) {
|
||||
val checkBox = checkBoxHolder.item
|
||||
fun CheckboxView(checkBox: SourceFiltersView.CheckBox) {
|
||||
val state by key(checkBox.hashCode()) { checkBox.state.collectAsState() }
|
||||
SourceFilterAction(
|
||||
name = checkBox.name,
|
||||
@@ -205,8 +187,7 @@ fun CheckboxView(checkBoxHolder: StableHolder<SourceFiltersView.CheckBox>) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HeaderView(headerHolder: StableHolder<SourceFiltersView.Header>) {
|
||||
val header = headerHolder.item
|
||||
fun HeaderView(header: SourceFiltersView.Header) {
|
||||
Box(Modifier.padding(horizontal = 16.dp, vertical = 8.dp).fillMaxWidth()) {
|
||||
Text(
|
||||
text = header.name,
|
||||
@@ -219,8 +200,7 @@ fun HeaderView(headerHolder: StableHolder<SourceFiltersView.Header>) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectView(selectHolder: StableHolder<SourceFiltersView.Select>) {
|
||||
val select = selectHolder.item
|
||||
fun SelectView(select: SourceFiltersView.Select) {
|
||||
val state by key(select.hashCode()) { select.state.collectAsState() }
|
||||
Row(
|
||||
Modifier.fillMaxWidth().defaultMinSize(minHeight = 56.dp)
|
||||
@@ -279,8 +259,7 @@ fun SortRow(name: String, selected: Boolean, asc: Boolean, onClick: () -> Unit)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SortView(sortHolder: StableHolder<SourceFiltersView.Sort>, startExpanded: Boolean, onExpandChanged: ((Boolean, Int) -> Unit)?) {
|
||||
val sort = sortHolder.item
|
||||
fun SortView(sort: SourceFiltersView.Sort, startExpanded: Boolean, onExpandChanged: ((Boolean, Int) -> Unit)?) {
|
||||
val state by key(sort.hashCode()) { sort.state.collectAsState() }
|
||||
ExpandablePreference(
|
||||
sort.name,
|
||||
@@ -313,8 +292,7 @@ fun SortView(sortHolder: StableHolder<SourceFiltersView.Sort>, startExpanded: Bo
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextView(textHolder: StableHolder<SourceFiltersView.Text>) {
|
||||
val text = textHolder.item
|
||||
fun TextView(text: SourceFiltersView.Text) {
|
||||
val placeholderText = remember(text) { text.filter.name }
|
||||
val state by key(text.hashCode()) { text.state.collectAsState() }
|
||||
var stateText by remember(text, state) {
|
||||
@@ -350,8 +328,7 @@ fun TextView(textHolder: StableHolder<SourceFiltersView.Text>) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TriStateView(triStateHolder: StableHolder<SourceFiltersView.TriState>) {
|
||||
val triState = triStateHolder.item
|
||||
fun TriStateView(triState: SourceFiltersView.TriState) {
|
||||
val state by key(triState.hashCode()) { triState.state.collectAsState() }
|
||||
SourceFilterAction(
|
||||
name = triState.name,
|
||||
|
||||
@@ -9,7 +9,6 @@ package ca.gosyer.jui.ui.sources.browse.filter
|
||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilter
|
||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilterChange
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.browse.filter.model.SourceFiltersView
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
@@ -49,7 +48,7 @@ class SourceFiltersViewModel(
|
||||
private val _loading = MutableStateFlow(true)
|
||||
val loading = _loading.asStateFlow()
|
||||
|
||||
private val _filters = MutableStateFlow<ImmutableList<StableHolder<SourceFiltersView<*, *>>>>(persistentListOf())
|
||||
private val _filters = MutableStateFlow<ImmutableList<SourceFiltersView<*, *>>>(persistentListOf())
|
||||
val filters = _filters.asStateFlow()
|
||||
|
||||
private val _showingFilters = MutableStateFlow(false)
|
||||
@@ -64,7 +63,7 @@ class SourceFiltersViewModel(
|
||||
filters.mapLatest { settings ->
|
||||
_filterButtonEnabled.value = settings.isNotEmpty()
|
||||
supervisorScope {
|
||||
settings.forEach { (filter) ->
|
||||
settings.forEach { filter ->
|
||||
if (filter is SourceFiltersView.Group) {
|
||||
filter.state.value.forEach { childFilter ->
|
||||
childFilter.state.drop(1)
|
||||
@@ -124,7 +123,7 @@ class SourceFiltersViewModel(
|
||||
|
||||
private fun List<SourceFilter>.toView() = mapIndexed { index, sourcePreference ->
|
||||
SourceFiltersView(index, sourcePreference)
|
||||
}.map(::StableHolder).toImmutableList()
|
||||
}.toImmutableList()
|
||||
|
||||
private companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -12,7 +12,6 @@ import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -59,7 +58,7 @@ class GlobalSearchViewModel @Inject constructor(
|
||||
val sources = combine(installedSources, languages) { installedSources, languages ->
|
||||
installedSources.filter {
|
||||
it.lang in languages || it.id == Source.LOCAL_SOURCE_ID
|
||||
}.map(::StableHolder).toImmutableList()
|
||||
}.toImmutableList()
|
||||
}.stateIn(scope, SharingStarted.Eagerly, persistentListOf())
|
||||
|
||||
private val search = MutableStateFlow(params.initialQuery)
|
||||
@@ -99,7 +98,7 @@ class GlobalSearchViewModel @Inject constructor(
|
||||
.mapLatest { (query, sources) ->
|
||||
results.clear()
|
||||
supervisorScope {
|
||||
sources.map { (source) ->
|
||||
sources.map { source ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
sourceHandler
|
||||
@@ -108,7 +107,7 @@ class GlobalSearchViewModel @Inject constructor(
|
||||
if (it.mangaList.isEmpty()) {
|
||||
Search.Failure(MR.strings.no_results_found.toPlatformString())
|
||||
} else {
|
||||
Search.Success(it.mangaList.map(::StableHolder).toImmutableList())
|
||||
Search.Success(it.mangaList.toImmutableList())
|
||||
}
|
||||
}
|
||||
.catch {
|
||||
@@ -143,7 +142,7 @@ class GlobalSearchViewModel @Inject constructor(
|
||||
|
||||
sealed class Search {
|
||||
object Searching : Search()
|
||||
data class Success(val mangaList: ImmutableList<StableHolder<Manga>>) : Search()
|
||||
data class Success(val mangaList: ImmutableList<Manga>) : Search()
|
||||
data class Failure(val e: String?) : Search() {
|
||||
constructor(e: Throwable) : this(e.message)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.unit.times
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
@@ -34,10 +33,9 @@ import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
@Composable
|
||||
fun GlobalSearchMangaComfortableGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
@@ -37,10 +36,9 @@ import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
@Composable
|
||||
fun GlobalSearchMangaCompactGridItem(
|
||||
modifier: Modifier,
|
||||
mangaHolder: StableHolder<Manga>,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val manga = mangaHolder.item
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
@@ -47,7 +47,6 @@ import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.components.localeToString
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.jui.ui.main.components.bottomNav
|
||||
import ca.gosyer.jui.ui.sources.globalsearch.GlobalSearchViewModel.Search
|
||||
@@ -63,7 +62,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun GlobalSearchScreenContent(
|
||||
sources: ImmutableList<StableHolder<Source>>,
|
||||
sources: ImmutableList<Source>,
|
||||
results: SnapshotStateMap<Long, Search>,
|
||||
displayMode: DisplayMode,
|
||||
query: String,
|
||||
@@ -91,17 +90,17 @@ fun GlobalSearchScreenContent(
|
||||
val state = rememberLazyListState()
|
||||
val sourcesSuccess by remember(sources) {
|
||||
derivedStateOf {
|
||||
sources.filter { results[it.item.id] is Search.Success }
|
||||
sources.filter { results[it.id] is Search.Success }
|
||||
}
|
||||
}
|
||||
val loadingSources by remember(sources) {
|
||||
derivedStateOf {
|
||||
sources.filter { results[it.item.id] == null }
|
||||
sources.filter { results[it.id] == null }
|
||||
}
|
||||
}
|
||||
val failedSources by remember(sources) {
|
||||
derivedStateOf {
|
||||
sources.filter { results[it.item.id] is Search.Failure }
|
||||
sources.filter { results[it.id] is Search.Failure }
|
||||
}
|
||||
}
|
||||
LazyColumn(
|
||||
@@ -115,8 +114,8 @@ fun GlobalSearchScreenContent(
|
||||
) {
|
||||
items(sourcesSuccess) {
|
||||
GlobalSearchItem(
|
||||
sourceHolder = it,
|
||||
search = results[it.item.id] ?: Search.Searching,
|
||||
source = it,
|
||||
search = results[it.id] ?: Search.Searching,
|
||||
displayMode = displayMode,
|
||||
onSourceClick = onSourceClick,
|
||||
onMangaClick = onMangaClick
|
||||
@@ -124,8 +123,8 @@ fun GlobalSearchScreenContent(
|
||||
}
|
||||
items(loadingSources) {
|
||||
GlobalSearchItem(
|
||||
sourceHolder = it,
|
||||
search = results[it.item.id] ?: Search.Searching,
|
||||
source = it,
|
||||
search = results[it.id] ?: Search.Searching,
|
||||
displayMode = displayMode,
|
||||
onSourceClick = onSourceClick,
|
||||
onMangaClick = onMangaClick
|
||||
@@ -133,8 +132,8 @@ fun GlobalSearchScreenContent(
|
||||
}
|
||||
items(failedSources) {
|
||||
GlobalSearchItem(
|
||||
sourceHolder = it,
|
||||
search = results[it.item.id] ?: Search.Searching,
|
||||
source = it,
|
||||
search = results[it.id] ?: Search.Searching,
|
||||
displayMode = displayMode,
|
||||
onSourceClick = onSourceClick,
|
||||
onMangaClick = onMangaClick
|
||||
@@ -160,13 +159,12 @@ fun GlobalSearchScreenContent(
|
||||
|
||||
@Composable
|
||||
fun GlobalSearchItem(
|
||||
sourceHolder: StableHolder<Source>,
|
||||
source: Source,
|
||||
search: Search,
|
||||
displayMode: DisplayMode,
|
||||
onSourceClick: (Source) -> Unit,
|
||||
onMangaClick: (Manga) -> Unit
|
||||
) {
|
||||
val source = sourceHolder.item
|
||||
Column {
|
||||
Row(
|
||||
Modifier.fillMaxWidth()
|
||||
@@ -209,15 +207,15 @@ fun GlobalSearchItem(
|
||||
items(search.mangaList) {
|
||||
if (displayMode == DisplayMode.ComfortableGrid) {
|
||||
GlobalSearchMangaComfortableGridItem(
|
||||
Modifier.clickable { onMangaClick(it.item) },
|
||||
Modifier.clickable { onMangaClick(it) },
|
||||
it,
|
||||
it.item.inLibrary
|
||||
it.inLibrary
|
||||
)
|
||||
} else {
|
||||
GlobalSearchMangaCompactGridItem(
|
||||
Modifier.clickable { onMangaClick(it.item) },
|
||||
Modifier.clickable { onMangaClick(it) },
|
||||
it,
|
||||
it.item.inLibrary
|
||||
it.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package ca.gosyer.jui.ui.sources.settings
|
||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreferenceChange
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.settings.model.SourceSettingsView
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
@@ -37,14 +36,14 @@ class SourceSettingsScreenViewModel @Inject constructor(
|
||||
private val _loading = MutableStateFlow(true)
|
||||
val loading = _loading.asStateFlow()
|
||||
|
||||
private val _sourceSettings = MutableStateFlow<ImmutableList<StableHolder<SourceSettingsView<*, *>>>>(persistentListOf())
|
||||
private val _sourceSettings = MutableStateFlow<ImmutableList<SourceSettingsView<*, *>>>(persistentListOf())
|
||||
val sourceSettings = _sourceSettings.asStateFlow()
|
||||
|
||||
init {
|
||||
getSourceSettings()
|
||||
sourceSettings.mapLatest { settings ->
|
||||
supervisorScope {
|
||||
settings.forEach { (setting) ->
|
||||
settings.forEach { setting ->
|
||||
setting.state.drop(1)
|
||||
.filterNotNull()
|
||||
.onEach {
|
||||
@@ -78,7 +77,7 @@ class SourceSettingsScreenViewModel @Inject constructor(
|
||||
|
||||
private fun List<SourcePreference>.toView() = mapIndexed { index, sourcePreference ->
|
||||
SourceSettingsView(index, sourcePreference)
|
||||
}.map(::StableHolder).toImmutableList()
|
||||
}.toImmutableList()
|
||||
|
||||
private companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.compose.ui.Modifier
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.presentation.build.BuildKonfig
|
||||
import ca.gosyer.jui.ui.base.dialog.getMaterialDialogProperties
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.jui.ui.base.prefs.ChoiceDialog
|
||||
import ca.gosyer.jui.ui.base.prefs.MultiSelectDialog
|
||||
@@ -61,7 +60,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun SourceSettingsScreenContent(
|
||||
settings: ImmutableList<StableHolder<SourceSettingsView<*, *>>>
|
||||
settings: ImmutableList<SourceSettingsView<*, *>>
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.windowInsetsPadding(
|
||||
@@ -84,20 +83,20 @@ fun SourceSettingsScreenContent(
|
||||
)
|
||||
).asPaddingValues()
|
||||
) {
|
||||
items(settings, { it.item.props.hashCode() }) {
|
||||
items(settings, { it.props.hashCode() }) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
when (it.item) {
|
||||
when (it) {
|
||||
is CheckBox, is Switch -> {
|
||||
TwoStatePreference(it as StableHolder<TwoState>, it.item is CheckBox)
|
||||
TwoStatePreference(it as TwoState, it is CheckBox)
|
||||
}
|
||||
is List -> {
|
||||
ListPreference(it as StableHolder<List>)
|
||||
ListPreference(it)
|
||||
}
|
||||
is EditText -> {
|
||||
EditTextPreference(it as StableHolder<EditText>)
|
||||
EditTextPreference(it)
|
||||
}
|
||||
is MultiSelect -> {
|
||||
MultiSelectPreference(it as StableHolder<MultiSelect>)
|
||||
MultiSelectPreference(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,8 +119,7 @@ fun SourceSettingsScreenContent(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TwoStatePreference(twoStateHolder: StableHolder<TwoState>, checkbox: Boolean) {
|
||||
val twoState = twoStateHolder.item
|
||||
private fun TwoStatePreference(twoState: TwoState, checkbox: Boolean) {
|
||||
val state by twoState.state.collectAsState()
|
||||
val title = remember(state) { twoState.title ?: twoState.summary ?: "No title" }
|
||||
val subtitle = remember(state) {
|
||||
@@ -146,8 +144,7 @@ private fun TwoStatePreference(twoStateHolder: StableHolder<TwoState>, checkbox:
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListPreference(listHolder: StableHolder<List>) {
|
||||
val list = listHolder.item
|
||||
private fun ListPreference(list: List) {
|
||||
val state by list.state.collectAsState()
|
||||
val title = remember(state) { list.title ?: list.summary ?: "No title" }
|
||||
val subtitle = remember(state) {
|
||||
@@ -175,8 +172,7 @@ private fun ListPreference(listHolder: StableHolder<List>) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MultiSelectPreference(multiSelectHolder: StableHolder<MultiSelect>) {
|
||||
val multiSelect = multiSelectHolder.item
|
||||
private fun MultiSelectPreference(multiSelect: MultiSelect) {
|
||||
val state by multiSelect.state.collectAsState()
|
||||
val title = remember(state) { multiSelect.title ?: multiSelect.summary ?: "No title" }
|
||||
val subtitle = remember(state) {
|
||||
@@ -205,8 +201,7 @@ private fun MultiSelectPreference(multiSelectHolder: StableHolder<MultiSelect>)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EditTextPreference(editTextHolder: StableHolder<EditText>) {
|
||||
val editText = editTextHolder.item
|
||||
private fun EditTextPreference(editText: EditText) {
|
||||
val state by editText.state.collectAsState()
|
||||
val title = remember(state) { editText.title ?: editText.summary ?: "No title" }
|
||||
val subtitle = remember(state) {
|
||||
|
||||
Reference in New Issue
Block a user