mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 23:02:04 +01:00
Library filtering and sorting modes
This commit is contained in:
@@ -9,12 +9,25 @@ package ca.gosyer.jui.data.library
|
|||||||
import ca.gosyer.jui.core.prefs.Preference
|
import ca.gosyer.jui.core.prefs.Preference
|
||||||
import ca.gosyer.jui.core.prefs.PreferenceStore
|
import ca.gosyer.jui.core.prefs.PreferenceStore
|
||||||
import ca.gosyer.jui.data.library.model.DisplayMode
|
import ca.gosyer.jui.data.library.model.DisplayMode
|
||||||
|
import ca.gosyer.jui.data.library.model.FilterState
|
||||||
import ca.gosyer.jui.data.library.model.Sort
|
import ca.gosyer.jui.data.library.model.Sort
|
||||||
|
|
||||||
class LibraryPreferences(private val preferenceStore: PreferenceStore) {
|
class LibraryPreferences(private val preferenceStore: PreferenceStore) {
|
||||||
|
|
||||||
fun displayMode(): Preference<DisplayMode> {
|
fun showAllCategory(): Preference<Boolean> {
|
||||||
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer())
|
return preferenceStore.getBoolean("show_all_category", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterDownloaded(): Preference<FilterState> {
|
||||||
|
return preferenceStore.getJsonObject("filter_downloaded", FilterState.IGNORED, FilterState.serializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterUnread(): Preference<FilterState> {
|
||||||
|
return preferenceStore.getJsonObject("filter_unread", FilterState.IGNORED, FilterState.serializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterCompleted(): Preference<FilterState> {
|
||||||
|
return preferenceStore.getJsonObject("filter_completed", FilterState.IGNORED, FilterState.serializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sortMode(): Preference<Sort> {
|
fun sortMode(): Preference<Sort> {
|
||||||
@@ -25,6 +38,10 @@ class LibraryPreferences(private val preferenceStore: PreferenceStore) {
|
|||||||
return preferenceStore.getBoolean("sort_ascending", true)
|
return preferenceStore.getBoolean("sort_ascending", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun displayMode(): Preference<DisplayMode> {
|
||||||
|
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer())
|
||||||
|
}
|
||||||
|
|
||||||
fun gridColumns(): Preference<Int> {
|
fun gridColumns(): Preference<Int> {
|
||||||
return preferenceStore.getInt("grid_columns", 0)
|
return preferenceStore.getInt("grid_columns", 0)
|
||||||
}
|
}
|
||||||
@@ -33,7 +50,19 @@ class LibraryPreferences(private val preferenceStore: PreferenceStore) {
|
|||||||
return preferenceStore.getInt("grid_size", 160)
|
return preferenceStore.getInt("grid_size", 160)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showAllCategory(): Preference<Boolean> {
|
fun unreadBadge(): Preference<Boolean> {
|
||||||
return preferenceStore.getBoolean("show_all_category", false)
|
return preferenceStore.getBoolean("unread_badge", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun downloadBadge(): Preference<Boolean> {
|
||||||
|
return preferenceStore.getBoolean("download_badge", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun languageBadge(): Preference<Boolean> {
|
||||||
|
return preferenceStore.getBoolean("language_badge", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun localBadge(): Preference<Boolean> {
|
||||||
|
return preferenceStore.getBoolean("local_badge", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ enum class DisplayMode(@Transient val res: StringResource) {
|
|||||||
List(MR.strings.display_list);
|
List(MR.strings.display_list);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val values = values()
|
val values = values().asList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.data.library.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class FilterState {
|
||||||
|
IGNORED,
|
||||||
|
INCLUDED,
|
||||||
|
EXCLUDED
|
||||||
|
}
|
||||||
@@ -6,18 +6,21 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.data.library.model
|
package ca.gosyer.jui.data.library.model
|
||||||
|
|
||||||
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import dev.icerock.moko.resources.StringResource
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
enum class Sort {
|
enum class Sort(@Transient val res: StringResource) {
|
||||||
ALPHABETICAL,
|
ALPHABETICAL(MR.strings.sort_alphabetical),
|
||||||
|
|
||||||
// LAST_READ,
|
// LAST_READ,
|
||||||
// LAST_CHECKED,
|
// LAST_CHECKED,
|
||||||
UNREAD,
|
UNREAD(MR.strings.sort_unread),
|
||||||
|
|
||||||
// TOTAL_CHAPTERS,
|
// TOTAL_CHAPTERS,
|
||||||
// LATEST_CHAPTER,
|
// LATEST_CHAPTER,
|
||||||
// DATE_FETCHED,
|
// DATE_FETCHED,
|
||||||
DATE_ADDED;
|
DATE_ADDED(MR.strings.sort_date_added);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<string name="action_more_actions">More actions</string>
|
<string name="action_more_actions">More actions</string>
|
||||||
<string name="action_ok">Ok</string>
|
<string name="action_ok">Ok</string>
|
||||||
<string name="action_browser">Browser</string>
|
<string name="action_browser">Browser</string>
|
||||||
|
<string name="action_filter">Filter</string>
|
||||||
|
|
||||||
<!-- Locations -->
|
<!-- Locations -->
|
||||||
<string name="location_library">Library</string>
|
<string name="location_library">Library</string>
|
||||||
@@ -81,6 +82,23 @@
|
|||||||
<string name="default_category">Default</string>
|
<string name="default_category">Default</string>
|
||||||
<string name="library_empty">Library is empty</string>
|
<string name="library_empty">Library is empty</string>
|
||||||
|
|
||||||
|
<string name="library_sort">Sort</string>
|
||||||
|
<string name="library_display">Display</string>
|
||||||
|
|
||||||
|
<string name="filter_downloaded">Downloaded</string>
|
||||||
|
<string name="filter_unread">Unread</string>
|
||||||
|
<string name="filter_completed">Completed</string>
|
||||||
|
|
||||||
|
<string name="sort_alphabetical">Alphabetically</string>
|
||||||
|
<string name="sort_unread">Unread</string>
|
||||||
|
<string name="sort_date_added">Date added</string>
|
||||||
|
|
||||||
|
<string name="display_badges">Badges</string>
|
||||||
|
<string name="display_badge_downloaded">Downloaded chapters</string>
|
||||||
|
<string name="display_badge_unread">Unread chapters</string>
|
||||||
|
<string name="display_badge_local">Local manga</string>
|
||||||
|
<string name="display_badge_language">Language</string>
|
||||||
|
|
||||||
<!-- Manga Menu -->
|
<!-- Manga Menu -->
|
||||||
<string name="page_progress">Page %1$d</string>
|
<string name="page_progress">Page %1$d</string>
|
||||||
<string name="no_chapters_found">No chapters found</string>
|
<string name="no_chapters_found">No chapters found</string>
|
||||||
@@ -100,7 +118,6 @@
|
|||||||
<string name="move_to_browse">Browse</string>
|
<string name="move_to_browse">Browse</string>
|
||||||
<string name="move_to_latest">Latest</string>
|
<string name="move_to_latest">Latest</string>
|
||||||
<string name="reset_filters">Reset</string>
|
<string name="reset_filters">Reset</string>
|
||||||
<string name="filter_source">Filter</string>
|
|
||||||
<string name="in_library">In library</string>
|
<string name="in_library">In library</string>
|
||||||
<string name="no_results_found">No results found</string>
|
<string name="no_results_found">No results found</string>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import ca.gosyer.jui.ui.categories.CategoriesScreenViewModel
|
|||||||
import ca.gosyer.jui.ui.downloads.DownloadsScreenViewModel
|
import ca.gosyer.jui.ui.downloads.DownloadsScreenViewModel
|
||||||
import ca.gosyer.jui.ui.extensions.ExtensionsScreenViewModel
|
import ca.gosyer.jui.ui.extensions.ExtensionsScreenViewModel
|
||||||
import ca.gosyer.jui.ui.library.LibraryScreenViewModel
|
import ca.gosyer.jui.ui.library.LibraryScreenViewModel
|
||||||
|
import ca.gosyer.jui.ui.library.settings.LibrarySettingsViewModel
|
||||||
import ca.gosyer.jui.ui.main.MainViewModel
|
import ca.gosyer.jui.ui.main.MainViewModel
|
||||||
import ca.gosyer.jui.ui.main.about.AboutViewModel
|
import ca.gosyer.jui.ui.main.about.AboutViewModel
|
||||||
import ca.gosyer.jui.ui.main.components.DebugOverlayViewModel
|
import ca.gosyer.jui.ui.main.components.DebugOverlayViewModel
|
||||||
@@ -43,6 +44,7 @@ actual class ViewModelFactoryImpl(
|
|||||||
private val downloadsFactory: (Boolean) -> DownloadsScreenViewModel,
|
private val downloadsFactory: (Boolean) -> DownloadsScreenViewModel,
|
||||||
private val extensionsFactory: () -> ExtensionsScreenViewModel,
|
private val extensionsFactory: () -> ExtensionsScreenViewModel,
|
||||||
private val libraryFactory: () -> LibraryScreenViewModel,
|
private val libraryFactory: () -> LibraryScreenViewModel,
|
||||||
|
private val librarySettingsFactory: () -> LibrarySettingsViewModel,
|
||||||
private val debugOverlayFactory: () -> DebugOverlayViewModel,
|
private val debugOverlayFactory: () -> DebugOverlayViewModel,
|
||||||
private val mainFactory: () -> MainViewModel,
|
private val mainFactory: () -> MainViewModel,
|
||||||
private val mangaFactory: (params: MangaScreenViewModel.Params) -> MangaScreenViewModel,
|
private val mangaFactory: (params: MangaScreenViewModel.Params) -> MangaScreenViewModel,
|
||||||
@@ -72,6 +74,7 @@ actual class ViewModelFactoryImpl(
|
|||||||
DownloadsScreenViewModel::class -> downloadsFactory(arg1 as Boolean)
|
DownloadsScreenViewModel::class -> downloadsFactory(arg1 as Boolean)
|
||||||
ExtensionsScreenViewModel::class -> extensionsFactory()
|
ExtensionsScreenViewModel::class -> extensionsFactory()
|
||||||
LibraryScreenViewModel::class -> libraryFactory()
|
LibraryScreenViewModel::class -> libraryFactory()
|
||||||
|
LibrarySettingsViewModel::class -> librarySettingsFactory()
|
||||||
DebugOverlayViewModel::class -> debugOverlayFactory()
|
DebugOverlayViewModel::class -> debugOverlayFactory()
|
||||||
MainViewModel::class -> mainFactory()
|
MainViewModel::class -> mainFactory()
|
||||||
MangaScreenViewModel::class -> mangaFactory(arg1 as MangaScreenViewModel.Params)
|
MangaScreenViewModel::class -> mangaFactory(arg1 as MangaScreenViewModel.Params)
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ package ca.gosyer.jui.ui.library
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import ca.gosyer.jui.ui.library.components.LibraryScreenContent
|
import ca.gosyer.jui.ui.library.components.LibraryScreenContent
|
||||||
|
import ca.gosyer.jui.ui.library.settings.LibrarySettingsViewModel
|
||||||
|
import ca.gosyer.jui.ui.library.settings.getLibraryDisplay
|
||||||
|
import ca.gosyer.jui.ui.library.settings.getLibraryFilters
|
||||||
|
import ca.gosyer.jui.ui.library.settings.getLibrarySort
|
||||||
import ca.gosyer.jui.ui.manga.MangaScreen
|
import ca.gosyer.jui.ui.manga.MangaScreen
|
||||||
import ca.gosyer.jui.uicore.vm.viewModel
|
import ca.gosyer.jui.uicore.vm.viewModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
@@ -24,6 +28,7 @@ class LibraryScreen : Screen {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val vm = viewModel<LibraryScreenViewModel>()
|
val vm = viewModel<LibraryScreenViewModel>()
|
||||||
|
val settingsVM = viewModel<LibrarySettingsViewModel>()
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
LibraryScreenContent(
|
LibraryScreenContent(
|
||||||
categories = vm.categories.collectAsState().value,
|
categories = vm.categories.collectAsState().value,
|
||||||
@@ -38,7 +43,16 @@ class LibraryScreen : Screen {
|
|||||||
getLibraryForPage = { vm.getLibraryForCategoryId(it).collectAsState() },
|
getLibraryForPage = { vm.getLibraryForCategoryId(it).collectAsState() },
|
||||||
onPageChanged = vm::setSelectedPage,
|
onPageChanged = vm::setSelectedPage,
|
||||||
onClickManga = { navigator push MangaScreen(it) },
|
onClickManga = { navigator push MangaScreen(it) },
|
||||||
onRemoveMangaClicked = vm::removeManga
|
onRemoveMangaClicked = vm::removeManga,
|
||||||
|
showingMenu = vm.showingMenu.collectAsState().value,
|
||||||
|
setShowingMenu = vm::setShowingMenu,
|
||||||
|
libraryFilters = getLibraryFilters(settingsVM),
|
||||||
|
librarySort = getLibrarySort(settingsVM),
|
||||||
|
libraryDisplay = getLibraryDisplay(settingsVM),
|
||||||
|
showUnread = vm.unreadBadges.collectAsState().value,
|
||||||
|
showDownloaded = vm.downloadBadges.collectAsState().value,
|
||||||
|
showLanguage = vm.languageBadges.collectAsState().value,
|
||||||
|
showLocal = vm.localBadges.collectAsState().value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,13 @@ package ca.gosyer.jui.ui.library
|
|||||||
import ca.gosyer.jui.core.lang.getDefault
|
import ca.gosyer.jui.core.lang.getDefault
|
||||||
import ca.gosyer.jui.core.lang.lowercase
|
import ca.gosyer.jui.core.lang.lowercase
|
||||||
import ca.gosyer.jui.core.lang.withDefaultContext
|
import ca.gosyer.jui.core.lang.withDefaultContext
|
||||||
|
import ca.gosyer.jui.core.prefs.getAsFlow
|
||||||
import ca.gosyer.jui.data.library.LibraryPreferences
|
import ca.gosyer.jui.data.library.LibraryPreferences
|
||||||
|
import ca.gosyer.jui.data.library.model.FilterState
|
||||||
import ca.gosyer.jui.data.library.model.Sort
|
import ca.gosyer.jui.data.library.model.Sort
|
||||||
import ca.gosyer.jui.data.models.Category
|
import ca.gosyer.jui.data.models.Category
|
||||||
import ca.gosyer.jui.data.models.Manga
|
import ca.gosyer.jui.data.models.Manga
|
||||||
|
import ca.gosyer.jui.data.models.MangaStatus
|
||||||
import ca.gosyer.jui.data.server.interactions.CategoryInteractionHandler
|
import ca.gosyer.jui.data.server.interactions.CategoryInteractionHandler
|
||||||
import ca.gosyer.jui.data.server.interactions.LibraryInteractionHandler
|
import ca.gosyer.jui.data.server.interactions.LibraryInteractionHandler
|
||||||
import ca.gosyer.jui.data.server.interactions.UpdatesInteractionHandler
|
import ca.gosyer.jui.data.server.interactions.UpdatesInteractionHandler
|
||||||
@@ -67,13 +70,42 @@ class LibraryScreenViewModel @Inject constructor(
|
|||||||
private val _selectedCategoryIndex = MutableStateFlow(0)
|
private val _selectedCategoryIndex = MutableStateFlow(0)
|
||||||
val selectedCategoryIndex = _selectedCategoryIndex.asStateFlow()
|
val selectedCategoryIndex = _selectedCategoryIndex.asStateFlow()
|
||||||
|
|
||||||
|
private val _showingMenu = MutableStateFlow(false)
|
||||||
|
val showingMenu = _showingMenu.asStateFlow()
|
||||||
|
|
||||||
val displayMode = libraryPreferences.displayMode().stateIn(scope)
|
val displayMode = libraryPreferences.displayMode().stateIn(scope)
|
||||||
val gridColumns = libraryPreferences.gridColumns().stateIn(scope)
|
val gridColumns = libraryPreferences.gridColumns().stateIn(scope)
|
||||||
val gridSize = libraryPreferences.gridSize().stateIn(scope)
|
val gridSize = libraryPreferences.gridSize().stateIn(scope)
|
||||||
|
val unreadBadges = libraryPreferences.unreadBadge().stateIn(scope)
|
||||||
|
val downloadBadges = libraryPreferences.downloadBadge().stateIn(scope)
|
||||||
|
val languageBadges = libraryPreferences.languageBadge().stateIn(scope)
|
||||||
|
val localBadges = libraryPreferences.localBadge().stateIn(scope)
|
||||||
|
|
||||||
private val sortMode = libraryPreferences.sortMode().stateIn(scope)
|
private val sortMode = libraryPreferences.sortMode().stateIn(scope)
|
||||||
private val sortAscending = libraryPreferences.sortAscending().stateIn(scope)
|
private val sortAscending = libraryPreferences.sortAscending().stateIn(scope)
|
||||||
|
|
||||||
|
private val filter = 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
|
||||||
|
FilterState.IGNORED -> true
|
||||||
|
} && when (unread) {
|
||||||
|
FilterState.EXCLUDED -> manga.unreadCount == null || manga.unreadCount == 0
|
||||||
|
FilterState.INCLUDED -> manga.unreadCount != null && (manga.unreadCount ?: 0) > 0
|
||||||
|
FilterState.IGNORED -> true
|
||||||
|
} && when (completed) {
|
||||||
|
FilterState.EXCLUDED -> manga.status != MangaStatus.COMPLETED
|
||||||
|
FilterState.INCLUDED -> manga.status == MangaStatus.COMPLETED
|
||||||
|
FilterState.IGNORED -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val _isLoading = MutableStateFlow(true)
|
private val _isLoading = MutableStateFlow(true)
|
||||||
val isLoading = _isLoading.asStateFlow()
|
val isLoading = _isLoading.asStateFlow()
|
||||||
|
|
||||||
@@ -114,6 +146,10 @@ class LibraryScreenViewModel @Inject constructor(
|
|||||||
_selectedCategoryIndex.value = page
|
_selectedCategoryIndex.value = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setShowingMenu(showingMenu: Boolean) {
|
||||||
|
_showingMenu.value = showingMenu
|
||||||
|
}
|
||||||
|
|
||||||
private fun getComparator(sortMode: Sort, ascending: Boolean): Comparator<Manga> {
|
private fun getComparator(sortMode: Sort, ascending: Boolean): Comparator<Manga> {
|
||||||
val sortFn = when (sortMode) {
|
val sortFn = when (sortMode) {
|
||||||
Sort.ALPHABETICAL -> {
|
Sort.ALPHABETICAL -> {
|
||||||
@@ -164,6 +200,8 @@ class LibraryScreenViewModel @Inject constructor(
|
|||||||
private fun getMangaItemsFlow(unfilteredItemsFlow: StateFlow<List<Manga>>): StateFlow<List<Manga>> {
|
private fun getMangaItemsFlow(unfilteredItemsFlow: StateFlow<List<Manga>>): StateFlow<List<Manga>> {
|
||||||
return combine(unfilteredItemsFlow, query) { unfilteredItems, query ->
|
return combine(unfilteredItemsFlow, query) { unfilteredItems, query ->
|
||||||
filterManga(query, unfilteredItems)
|
filterManga(query, unfilteredItems)
|
||||||
|
}.combine(filter) { filteredManga, filterer ->
|
||||||
|
filteredManga.filter(filterer)
|
||||||
}.combine(comparator) { filteredManga, comparator ->
|
}.combine(comparator) { filteredManga, comparator ->
|
||||||
filteredManga.sortedWith(comparator)
|
filteredManga.sortedWith(comparator)
|
||||||
}.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
}.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
package ca.gosyer.jui.ui.library.components
|
package ca.gosyer.jui.ui.library.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
@@ -15,33 +18,66 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ca.gosyer.jui.data.models.Manga
|
||||||
|
import ca.gosyer.jui.data.models.Source
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LibraryMangaBadges(
|
fun LibraryMangaBadges(
|
||||||
unread: Int?,
|
|
||||||
downloaded: Int?,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
manga: Manga,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
if (unread == null && downloaded == null) return
|
val unread = manga.unreadCount
|
||||||
|
val downloaded = manga.downloadCount
|
||||||
|
val isLocal = manga.sourceId == Source.LOCAL_SOURCE_ID
|
||||||
|
|
||||||
Row(modifier = modifier.clip(MaterialTheme.shapes.medium)) {
|
Row(modifier then Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
if (unread != null && unread > 0) {
|
if ((unread != null && unread > 0) || (downloaded != null && downloaded > 0) || isLocal) {
|
||||||
Text(
|
Row(modifier = Modifier.clip(MaterialTheme.shapes.medium)) {
|
||||||
text = unread.toString(),
|
if (showLocal && isLocal) {
|
||||||
modifier = Modifier.background(MaterialTheme.colors.primary).then(BadgesInnerPadding),
|
Text(
|
||||||
style = MaterialTheme.typography.caption,
|
text = unread.toString(),
|
||||||
color = MaterialTheme.colors.onPrimary
|
modifier = Modifier.background(MaterialTheme.colors.secondary).then(BadgesInnerPadding),
|
||||||
)
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = MaterialTheme.colors.onSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showUnread && unread != null && unread > 0) {
|
||||||
|
Text(
|
||||||
|
text = unread.toString(),
|
||||||
|
modifier = Modifier.background(MaterialTheme.colors.primary).then(BadgesInnerPadding),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = MaterialTheme.colors.onPrimary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showDownloaded && downloaded != null && downloaded > 0) {
|
||||||
|
Text(
|
||||||
|
text = downloaded.toString(),
|
||||||
|
modifier = Modifier.background(MaterialTheme.colors.secondary).then(
|
||||||
|
BadgesInnerPadding
|
||||||
|
),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = MaterialTheme.colors.onSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Spacer(Modifier)
|
||||||
}
|
}
|
||||||
if (downloaded != null && downloaded > 0) {
|
|
||||||
Text(
|
val lang = manga.source?.lang
|
||||||
text = downloaded.toString(),
|
if (showLanguage && lang != null) {
|
||||||
modifier = Modifier.background(MaterialTheme.colors.secondary).then(
|
Row(modifier = Modifier.clip(MaterialTheme.shapes.medium)) {
|
||||||
BadgesInnerPadding
|
Text(
|
||||||
),
|
text = lang.uppercase(),
|
||||||
style = MaterialTheme.typography.caption,
|
modifier = Modifier.background(MaterialTheme.colors.secondary).then(BadgesInnerPadding),
|
||||||
color = MaterialTheme.colors.onSecondary
|
style = MaterialTheme.typography.caption,
|
||||||
)
|
color = MaterialTheme.colors.onSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
package ca.gosyer.jui.ui.library.components
|
package ca.gosyer.jui.ui.library.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.requiredHeight
|
import androidx.compose.foundation.layout.requiredHeight
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
@@ -34,7 +36,11 @@ import io.kamel.image.lazyPainterResource
|
|||||||
fun LibraryMangaList(
|
fun LibraryMangaList(
|
||||||
library: List<Manga>,
|
library: List<Manga>,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
@@ -49,8 +55,10 @@ fun LibraryMangaList(
|
|||||||
{ onRemoveMangaClicked(manga.id) }
|
{ onRemoveMangaClicked(manga.id) }
|
||||||
),
|
),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
unread = manga.unreadCount,
|
showUnread = showUnread,
|
||||||
downloaded = manga.downloadCount,
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,8 +75,10 @@ fun LibraryMangaList(
|
|||||||
private fun LibraryMangaListItem(
|
private fun LibraryMangaListItem(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
unread: Int?,
|
showUnread: Boolean,
|
||||||
downloaded: Int?,
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||||
MangaListItem(
|
MangaListItem(
|
||||||
@@ -89,6 +99,14 @@ private fun LibraryMangaListItem(
|
|||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
text = manga.title,
|
text = manga.title,
|
||||||
)
|
)
|
||||||
LibraryMangaBadges(unread, downloaded)
|
Box(Modifier.width(IntrinsicSize.Min)) {
|
||||||
|
LibraryMangaBadges(
|
||||||
|
manga = manga,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ fun LibraryPager(
|
|||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
if (categories.isEmpty()) return
|
if (categories.isEmpty()) return
|
||||||
|
|
||||||
@@ -37,26 +41,42 @@ fun LibraryPager(
|
|||||||
gridColumns = gridColumns,
|
gridColumns = gridColumns,
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
DisplayMode.ComfortableGrid -> LibraryMangaComfortableGrid(
|
DisplayMode.ComfortableGrid -> LibraryMangaComfortableGrid(
|
||||||
library = library,
|
library = library,
|
||||||
gridColumns = gridColumns,
|
gridColumns = gridColumns,
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
DisplayMode.CoverOnlyGrid -> LibraryMangaCoverOnlyGrid(
|
DisplayMode.CoverOnlyGrid -> LibraryMangaCoverOnlyGrid(
|
||||||
library = library,
|
library = library,
|
||||||
gridColumns = gridColumns,
|
gridColumns = gridColumns,
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
DisplayMode.List -> LibraryMangaList(
|
DisplayMode.List -> LibraryMangaList(
|
||||||
library = library,
|
library = library,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
else -> Box {}
|
else -> Box {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,23 +6,40 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.ui.library.components
|
package ca.gosyer.jui.ui.library.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInHorizontally
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.foundation.gestures.forEachGesture
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.BottomSheetScaffold
|
import androidx.compose.material.ModalBottomSheetLayout
|
||||||
|
import androidx.compose.material.ModalBottomSheetValue
|
||||||
import androidx.compose.material.Scaffold
|
import androidx.compose.material.Scaffold
|
||||||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.FilterList
|
||||||
|
import androidx.compose.material.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ca.gosyer.jui.data.library.model.DisplayMode
|
import ca.gosyer.jui.data.library.model.DisplayMode
|
||||||
import ca.gosyer.jui.data.models.Category
|
import ca.gosyer.jui.data.models.Category
|
||||||
import ca.gosyer.jui.data.models.Manga
|
import ca.gosyer.jui.data.models.Manga
|
||||||
import ca.gosyer.jui.i18n.MR
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import ca.gosyer.jui.ui.base.navigation.ActionItem
|
||||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||||
|
import ca.gosyer.jui.ui.library.settings.LibrarySheet
|
||||||
|
import ca.gosyer.jui.ui.library.settings.LibrarySideMenu
|
||||||
import ca.gosyer.jui.uicore.components.LoadingScreen
|
import ca.gosyer.jui.uicore.components.LoadingScreen
|
||||||
import ca.gosyer.jui.uicore.resources.stringResource
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
@@ -42,7 +59,16 @@ fun LibraryScreenContent(
|
|||||||
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
||||||
onPageChanged: (Int) -> Unit,
|
onPageChanged: (Int) -> Unit,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showingMenu: Boolean,
|
||||||
|
setShowingMenu: (Boolean) -> Unit,
|
||||||
|
libraryFilters: @Composable () -> Unit,
|
||||||
|
librarySort: @Composable () -> Unit,
|
||||||
|
libraryDisplay: @Composable () -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
BoxWithConstraints {
|
BoxWithConstraints {
|
||||||
val pagerState = rememberPagerState(selectedCategoryIndex)
|
val pagerState = rememberPagerState(selectedCategoryIndex)
|
||||||
@@ -71,7 +97,16 @@ fun LibraryScreenContent(
|
|||||||
getLibraryForPage = getLibraryForPage,
|
getLibraryForPage = getLibraryForPage,
|
||||||
onPageChanged = onPageChanged,
|
onPageChanged = onPageChanged,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showingMenu = showingMenu,
|
||||||
|
setShowingMenu = setShowingMenu,
|
||||||
|
libraryFilters = libraryFilters,
|
||||||
|
librarySort = librarySort,
|
||||||
|
libraryDisplay = libraryDisplay,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ThinLibraryScreenContent(
|
ThinLibraryScreenContent(
|
||||||
@@ -88,7 +123,16 @@ fun LibraryScreenContent(
|
|||||||
getLibraryForPage = getLibraryForPage,
|
getLibraryForPage = getLibraryForPage,
|
||||||
onPageChanged = onPageChanged,
|
onPageChanged = onPageChanged,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showingSheet = showingMenu,
|
||||||
|
setShowingSheet = setShowingMenu,
|
||||||
|
libraryFilters = libraryFilters,
|
||||||
|
librarySort = librarySort,
|
||||||
|
libraryDisplay = libraryDisplay,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +153,16 @@ fun WideLibraryScreenContent(
|
|||||||
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
||||||
onPageChanged: (Int) -> Unit,
|
onPageChanged: (Int) -> Unit,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showingMenu: Boolean,
|
||||||
|
setShowingMenu: (Boolean) -> Unit,
|
||||||
|
libraryFilters: @Composable () -> Unit,
|
||||||
|
librarySort: @Composable () -> Unit,
|
||||||
|
libraryDisplay: @Composable () -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -117,7 +170,12 @@ fun WideLibraryScreenContent(
|
|||||||
Toolbar(
|
Toolbar(
|
||||||
stringResource(MR.strings.location_library),
|
stringResource(MR.strings.location_library),
|
||||||
searchText = query,
|
searchText = query,
|
||||||
search = updateQuery
|
search = updateQuery,
|
||||||
|
actions = {
|
||||||
|
getActionItems(
|
||||||
|
onToggleFiltersClick = { setShowingMenu(true) }
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
LibraryTabs(
|
LibraryTabs(
|
||||||
visible = true, // vm.showCategoryTabs,
|
visible = true, // vm.showCategoryTabs,
|
||||||
@@ -128,8 +186,8 @@ fun WideLibraryScreenContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) { padding ->
|
||||||
Box(Modifier.padding(it)) {
|
Box(Modifier.padding(padding)) {
|
||||||
if (categories.isEmpty()) {
|
if (categories.isEmpty()) {
|
||||||
LoadingScreen(isLoading, errorMessage = error)
|
LoadingScreen(isLoading, errorMessage = error)
|
||||||
} else {
|
} else {
|
||||||
@@ -141,8 +199,36 @@ fun WideLibraryScreenContent(
|
|||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
getLibraryForPage = getLibraryForPage,
|
getLibraryForPage = getLibraryForPage,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (showingMenu) {
|
||||||
|
Box(
|
||||||
|
Modifier.fillMaxSize().pointerInput(isLoading) {
|
||||||
|
forEachGesture {
|
||||||
|
detectTapGestures {
|
||||||
|
setShowingMenu(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
showingMenu,
|
||||||
|
enter = fadeIn() + slideInHorizontally(initialOffsetX = { it * 2 }),
|
||||||
|
exit = fadeOut() + slideOutHorizontally(targetOffsetX = { it * 2 }),
|
||||||
|
modifier = Modifier.align(Alignment.TopEnd)
|
||||||
|
) {
|
||||||
|
LibrarySideMenu(
|
||||||
|
libraryFilters = libraryFilters,
|
||||||
|
librarySort = librarySort,
|
||||||
|
libraryDisplay = libraryDisplay
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,17 +249,47 @@ fun ThinLibraryScreenContent(
|
|||||||
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
||||||
onPageChanged: (Int) -> Unit,
|
onPageChanged: (Int) -> Unit,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showingSheet: Boolean,
|
||||||
|
setShowingSheet: (Boolean) -> Unit,
|
||||||
|
libraryFilters: @Composable () -> Unit,
|
||||||
|
librarySort: @Composable () -> Unit,
|
||||||
|
libraryDisplay: @Composable () -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
val sheetState = rememberBottomSheetScaffoldState()
|
val bottomSheetState = rememberModalBottomSheetState(
|
||||||
BottomSheetScaffold(
|
ModalBottomSheetValue.Hidden,
|
||||||
scaffoldState = sheetState,
|
confirmStateChange = {
|
||||||
|
when (it) {
|
||||||
|
ModalBottomSheetValue.Hidden -> setShowingSheet(false)
|
||||||
|
ModalBottomSheetValue.Expanded,
|
||||||
|
ModalBottomSheetValue.HalfExpanded -> setShowingSheet(true)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
LaunchedEffect(showingSheet) {
|
||||||
|
if (showingSheet) {
|
||||||
|
bottomSheetState.show()
|
||||||
|
} else {
|
||||||
|
bottomSheetState.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
Toolbar(
|
Toolbar(
|
||||||
stringResource(MR.strings.location_library),
|
stringResource(MR.strings.location_library),
|
||||||
searchText = query,
|
searchText = query,
|
||||||
search = updateQuery
|
search = updateQuery,
|
||||||
|
actions = {
|
||||||
|
getActionItems(
|
||||||
|
onToggleFiltersClick = { setShowingSheet(true) }
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
LibraryTabs(
|
LibraryTabs(
|
||||||
visible = true, // vm.showCategoryTabs,
|
visible = true, // vm.showCategoryTabs,
|
||||||
@@ -183,13 +299,19 @@ fun ThinLibraryScreenContent(
|
|||||||
onPageChanged = onPageChanged
|
onPageChanged = onPageChanged
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
sheetContent = {
|
) { padding ->
|
||||||
// LibrarySheetContent()
|
ModalBottomSheetLayout(
|
||||||
},
|
sheetState = bottomSheetState,
|
||||||
sheetPeekHeight = 0.dp
|
modifier = Modifier.padding(padding),
|
||||||
) {
|
sheetContent = {
|
||||||
Box(Modifier.padding(it)) {
|
LibrarySheet(
|
||||||
|
libraryFilters = libraryFilters,
|
||||||
|
librarySort = librarySort,
|
||||||
|
libraryDisplay = libraryDisplay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
if (categories.isEmpty()) {
|
if (categories.isEmpty()) {
|
||||||
LoadingScreen(isLoading, errorMessage = error)
|
LoadingScreen(isLoading, errorMessage = error)
|
||||||
} else {
|
} else {
|
||||||
@@ -201,9 +323,27 @@ fun ThinLibraryScreenContent(
|
|||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
getLibraryForPage = getLibraryForPage,
|
getLibraryForPage = getLibraryForPage,
|
||||||
onClickManga = onClickManga,
|
onClickManga = onClickManga,
|
||||||
onRemoveMangaClicked = onRemoveMangaClicked
|
onRemoveMangaClicked = onRemoveMangaClicked,
|
||||||
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
private fun getActionItems(
|
||||||
|
onToggleFiltersClick: () -> Unit,
|
||||||
|
): List<ActionItem> {
|
||||||
|
return listOfNotNull(
|
||||||
|
ActionItem(
|
||||||
|
name = stringResource(MR.strings.action_filter),
|
||||||
|
icon = Icons.Rounded.FilterList,
|
||||||
|
doAction = onToggleFiltersClick
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ fun LibraryMangaComfortableGrid(
|
|||||||
gridColumns: Int,
|
gridColumns: Int,
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
@@ -64,8 +68,10 @@ fun LibraryMangaComfortableGrid(
|
|||||||
{ onRemoveMangaClicked(manga.id) }
|
{ onRemoveMangaClicked(manga.id) }
|
||||||
),
|
),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
unread = manga.unreadCount,
|
showUnread = showUnread,
|
||||||
downloaded = manga.downloadCount
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,8 +88,10 @@ fun LibraryMangaComfortableGrid(
|
|||||||
private fun LibraryMangaComfortableGridItem(
|
private fun LibraryMangaComfortableGridItem(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
unread: Int?,
|
showUnread: Boolean,
|
||||||
downloaded: Int?
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||||
val fontStyle = LocalTextStyle.current.merge(
|
val fontStyle = LocalTextStyle.current.merge(
|
||||||
@@ -114,9 +122,12 @@ private fun LibraryMangaComfortableGridItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
LibraryMangaBadges(
|
LibraryMangaBadges(
|
||||||
unread = unread,
|
modifier = Modifier.padding(4.dp),
|
||||||
downloaded = downloaded,
|
manga = manga,
|
||||||
modifier = Modifier.padding(4.dp)
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,11 @@ fun LibraryMangaCompactGrid(
|
|||||||
gridColumns: Int,
|
gridColumns: Int,
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
@@ -72,8 +76,10 @@ fun LibraryMangaCompactGrid(
|
|||||||
{ onRemoveMangaClicked(manga.id) }
|
{ onRemoveMangaClicked(manga.id) }
|
||||||
),
|
),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
unread = manga.unreadCount,
|
showUnread = showUnread,
|
||||||
downloaded = manga.downloadCount
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,8 +96,10 @@ fun LibraryMangaCompactGrid(
|
|||||||
private fun LibraryMangaCompactGridItem(
|
private fun LibraryMangaCompactGridItem(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
unread: Int?,
|
showUnread: Boolean,
|
||||||
downloaded: Int?,
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||||
val fontStyle = LocalTextStyle.current.merge(
|
val fontStyle = LocalTextStyle.current.merge(
|
||||||
@@ -119,9 +127,12 @@ private fun LibraryMangaCompactGridItem(
|
|||||||
modifier = Modifier.align(Alignment.BottomStart).padding(8.dp)
|
modifier = Modifier.align(Alignment.BottomStart).padding(8.dp)
|
||||||
)
|
)
|
||||||
LibraryMangaBadges(
|
LibraryMangaBadges(
|
||||||
unread = unread,
|
modifier = Modifier.padding(4.dp),
|
||||||
downloaded = downloaded,
|
manga = manga,
|
||||||
modifier = Modifier.padding(4.dp)
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ fun LibraryMangaCoverOnlyGrid(
|
|||||||
gridColumns: Int,
|
gridColumns: Int,
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
onClickManga: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onRemoveMangaClicked: (Long) -> Unit
|
onRemoveMangaClicked: (Long) -> Unit,
|
||||||
|
showUnread: Boolean,
|
||||||
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
@@ -58,8 +62,10 @@ fun LibraryMangaCoverOnlyGrid(
|
|||||||
{ onRemoveMangaClicked(manga.id) }
|
{ onRemoveMangaClicked(manga.id) }
|
||||||
),
|
),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
unread = manga.unreadCount,
|
showUnread = showUnread,
|
||||||
downloaded = manga.downloadCount
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,8 +82,10 @@ fun LibraryMangaCoverOnlyGrid(
|
|||||||
private fun LibraryMangaCoverOnlyGridItem(
|
private fun LibraryMangaCoverOnlyGridItem(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
unread: Int?,
|
showUnread: Boolean,
|
||||||
downloaded: Int?
|
showDownloaded: Boolean,
|
||||||
|
showLanguage: Boolean,
|
||||||
|
showLocal: Boolean
|
||||||
) {
|
) {
|
||||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||||
|
|
||||||
@@ -94,9 +102,12 @@ private fun LibraryMangaCoverOnlyGridItem(
|
|||||||
contentScale = ContentScale.Crop
|
contentScale = ContentScale.Crop
|
||||||
)
|
)
|
||||||
LibraryMangaBadges(
|
LibraryMangaBadges(
|
||||||
unread = unread,
|
modifier = Modifier.padding(4.dp),
|
||||||
downloaded = downloaded,
|
manga = manga,
|
||||||
modifier = Modifier.padding(4.dp)
|
showUnread = showUnread,
|
||||||
|
showDownloaded = showDownloaded,
|
||||||
|
showLanguage = showLanguage,
|
||||||
|
showLocal = showLocal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.Checkbox
|
||||||
|
import androidx.compose.material.RadioButton
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.util.fastForEach
|
||||||
|
import ca.gosyer.jui.data.library.model.DisplayMode
|
||||||
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import ca.gosyer.jui.ui.sources.browse.filter.SourceFilterAction
|
||||||
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getLibraryDisplay(vm: LibrarySettingsViewModel): @Composable () -> Unit = remember(vm) {
|
||||||
|
@Composable {
|
||||||
|
LibraryDisplay(
|
||||||
|
displayMode = vm.displayMode.collectAsState().value,
|
||||||
|
unreadBadges = vm.unreadBadges.collectAsState().value,
|
||||||
|
downloadBadges = vm.downloadBadges.collectAsState().value,
|
||||||
|
languageBadges = vm.languageBadges.collectAsState().value,
|
||||||
|
localBadges = vm.localBadges.collectAsState().value,
|
||||||
|
setDisplayMode = { vm.displayMode.value = it },
|
||||||
|
setUnreadBadges = { vm.unreadBadges.value = it },
|
||||||
|
setDownloadBadges = { vm.downloadBadges.value = it },
|
||||||
|
setLanguageBadges = { vm.languageBadges.value = it },
|
||||||
|
setLocalBadges = { vm.localBadges.value = it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LibraryDisplay(
|
||||||
|
displayMode: DisplayMode,
|
||||||
|
unreadBadges: Boolean,
|
||||||
|
downloadBadges: Boolean,
|
||||||
|
languageBadges: Boolean,
|
||||||
|
localBadges: Boolean,
|
||||||
|
setDisplayMode: (DisplayMode) -> Unit,
|
||||||
|
setUnreadBadges: (Boolean) -> Unit,
|
||||||
|
setDownloadBadges: (Boolean) -> Unit,
|
||||||
|
setLanguageBadges: (Boolean) -> Unit,
|
||||||
|
setLocalBadges: (Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
Column(Modifier.fillMaxWidth()) {
|
||||||
|
TitleText(stringResource(MR.strings.display_mode))
|
||||||
|
DisplayMode.values.fastForEach {
|
||||||
|
RadioSelectionItem(
|
||||||
|
text = stringResource(it.res),
|
||||||
|
selected = it == displayMode,
|
||||||
|
onClick = { setDisplayMode(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TitleText(stringResource(MR.strings.display_badges))
|
||||||
|
CheckboxItem(
|
||||||
|
text = stringResource(MR.strings.display_badge_downloaded),
|
||||||
|
checked = downloadBadges,
|
||||||
|
onClick = { setDownloadBadges(!downloadBadges) }
|
||||||
|
)
|
||||||
|
CheckboxItem(
|
||||||
|
text = stringResource(MR.strings.display_badge_unread),
|
||||||
|
checked = unreadBadges,
|
||||||
|
onClick = { setUnreadBadges(!unreadBadges) }
|
||||||
|
)
|
||||||
|
CheckboxItem(
|
||||||
|
text = stringResource(MR.strings.display_badge_local),
|
||||||
|
checked = localBadges,
|
||||||
|
onClick = { setLocalBadges(!localBadges) }
|
||||||
|
)
|
||||||
|
// TODO: 2022-04-06 Enable when library contains manga source in manga object
|
||||||
|
/*CheckboxItem(
|
||||||
|
text = stringResource(MR.strings.display_badge_language),
|
||||||
|
checked = languageBadges,
|
||||||
|
onClick = { setLanguageBadges(!languageBadges) }
|
||||||
|
)*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TitleText(text: String) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RadioSelectionItem(text: String, selected: Boolean, onClick: () -> Unit) {
|
||||||
|
SourceFilterAction(
|
||||||
|
name = text,
|
||||||
|
onClick = onClick,
|
||||||
|
action = {
|
||||||
|
RadioButton(
|
||||||
|
selected = selected,
|
||||||
|
onClick = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CheckboxItem(text: String, checked: Boolean, onClick: () -> Unit) {
|
||||||
|
SourceFilterAction(
|
||||||
|
name = text,
|
||||||
|
onClick = onClick,
|
||||||
|
action = {
|
||||||
|
Checkbox(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material.TriStateCheckbox
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.state.ToggleableState
|
||||||
|
import ca.gosyer.jui.data.library.model.FilterState
|
||||||
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import ca.gosyer.jui.ui.sources.browse.filter.SourceFilterAction
|
||||||
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getLibraryFilters(vm: LibrarySettingsViewModel): @Composable () -> Unit = remember(vm) {
|
||||||
|
@Composable {
|
||||||
|
LibraryFilters(
|
||||||
|
downloaded = vm.filterDownloaded.collectAsState().value,
|
||||||
|
unread = vm.filterUnread.collectAsState().value,
|
||||||
|
completed = vm.filterCompleted.collectAsState().value,
|
||||||
|
setDownloadedFilter = { vm.filterDownloaded.value = it },
|
||||||
|
setUnreadFilter = { vm.filterUnread.value = it },
|
||||||
|
setCompletedFilter = { vm.filterCompleted.value = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LibraryFilters(
|
||||||
|
downloaded: FilterState,
|
||||||
|
unread: FilterState,
|
||||||
|
completed: FilterState,
|
||||||
|
setDownloadedFilter: (FilterState) -> Unit,
|
||||||
|
setUnreadFilter: (FilterState) -> Unit,
|
||||||
|
setCompletedFilter: (FilterState) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(Modifier.fillMaxWidth()) {
|
||||||
|
Filter(
|
||||||
|
stringResource(MR.strings.filter_downloaded),
|
||||||
|
downloaded,
|
||||||
|
onClick = { setDownloadedFilter(toggleState(downloaded)) }
|
||||||
|
)
|
||||||
|
Filter(
|
||||||
|
stringResource(MR.strings.filter_unread),
|
||||||
|
unread,
|
||||||
|
onClick = { setUnreadFilter(toggleState(unread)) }
|
||||||
|
)
|
||||||
|
Filter(
|
||||||
|
stringResource(MR.strings.filter_completed),
|
||||||
|
completed,
|
||||||
|
onClick = { setCompletedFilter(toggleState(completed)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleState(filterState: FilterState) = when (filterState) {
|
||||||
|
FilterState.IGNORED -> FilterState.INCLUDED
|
||||||
|
FilterState.INCLUDED -> FilterState.EXCLUDED
|
||||||
|
FilterState.EXCLUDED -> FilterState.IGNORED
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Filter(text: String, state: FilterState, onClick: () -> Unit) {
|
||||||
|
SourceFilterAction(
|
||||||
|
text,
|
||||||
|
onClick = onClick,
|
||||||
|
action = {
|
||||||
|
TriStateCheckbox(
|
||||||
|
state = when (state) {
|
||||||
|
FilterState.INCLUDED -> ToggleableState.On
|
||||||
|
FilterState.EXCLUDED -> ToggleableState.Indeterminate
|
||||||
|
FilterState.IGNORED -> ToggleableState.Off
|
||||||
|
},
|
||||||
|
onClick = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import ca.gosyer.jui.data.library.LibraryPreferences
|
||||||
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
|
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||||
|
import me.tatarka.inject.annotations.Inject
|
||||||
|
|
||||||
|
class LibrarySettingsViewModel @Inject constructor(
|
||||||
|
libraryPreferences: LibraryPreferences,
|
||||||
|
contextWrapper: ContextWrapper
|
||||||
|
) : ViewModel(contextWrapper) {
|
||||||
|
val filterDownloaded = libraryPreferences.filterDownloaded().asStateFlow()
|
||||||
|
val filterUnread = libraryPreferences.filterUnread().asStateFlow()
|
||||||
|
val filterCompleted = libraryPreferences.filterCompleted().asStateFlow()
|
||||||
|
|
||||||
|
val sortMode = libraryPreferences.sortMode().asStateFlow()
|
||||||
|
val sortAscending = libraryPreferences.sortAscending().asStateFlow()
|
||||||
|
|
||||||
|
val displayMode = libraryPreferences.displayMode().asStateFlow()
|
||||||
|
val unreadBadges = libraryPreferences.unreadBadge().asStateFlow()
|
||||||
|
val downloadBadges = libraryPreferences.downloadBadge().asStateFlow()
|
||||||
|
val languageBadges = libraryPreferences.languageBadge().asStateFlow()
|
||||||
|
val localBadges = libraryPreferences.localBadge().asStateFlow()
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.Tab
|
||||||
|
import androidx.compose.material.TabRow
|
||||||
|
import androidx.compose.material.TabRowDefaults
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||||
|
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||||
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
|
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||||
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
enum class LibrarySheetTabs(val res: StringResource) {
|
||||||
|
FILTERS(MR.strings.action_filter),
|
||||||
|
SORT(MR.strings.library_sort),
|
||||||
|
DISPLAY(MR.strings.library_display)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LibrarySheet(
|
||||||
|
libraryFilters: @Composable () -> Unit,
|
||||||
|
librarySort: @Composable () -> Unit,
|
||||||
|
libraryDisplay: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val pagerState = rememberPagerState()
|
||||||
|
val selectedPage = pagerState.currentPage
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
Column(Modifier.fillMaxSize()) {
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = pagerState.currentPage,
|
||||||
|
indicator = { tabPositions ->
|
||||||
|
TabRowDefaults.Indicator(
|
||||||
|
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
LibrarySheetTabs.values().asList().fastForEachIndexed { index, tab ->
|
||||||
|
Tab(
|
||||||
|
selected = selectedPage == index,
|
||||||
|
onClick = {
|
||||||
|
scope.launch { pagerState.animateScrollToPage(index) }
|
||||||
|
},
|
||||||
|
text = { Text(stringResource(tab.res)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalPager(
|
||||||
|
count = LibrarySheetTabs.values().size,
|
||||||
|
state = pagerState,
|
||||||
|
verticalAlignment = Alignment.Top
|
||||||
|
) {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Box {
|
||||||
|
Column(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
) {
|
||||||
|
when (it) {
|
||||||
|
LibrarySheetTabs.FILTERS.ordinal -> libraryFilters()
|
||||||
|
LibrarySheetTabs.SORT.ordinal -> librarySort()
|
||||||
|
LibrarySheetTabs.DISPLAY.ordinal -> libraryDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalScrollbar(
|
||||||
|
rememberScrollbarAdapter(scrollState),
|
||||||
|
Modifier.align(Alignment.CenterEnd)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.Divider
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import ca.gosyer.jui.i18n.MR
|
||||||
|
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||||
|
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||||
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LibrarySideMenu(
|
||||||
|
libraryFilters: @Composable () -> Unit,
|
||||||
|
librarySort: @Composable () -> Unit,
|
||||||
|
libraryDisplay: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
Surface(Modifier.fillMaxHeight().width(260.dp), elevation = 1.dp) {
|
||||||
|
Box {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
) {
|
||||||
|
TitleText(stringResource(MR.strings.action_filter))
|
||||||
|
libraryFilters()
|
||||||
|
Divider()
|
||||||
|
TitleText(stringResource(MR.strings.library_sort))
|
||||||
|
librarySort()
|
||||||
|
Divider()
|
||||||
|
TitleText(stringResource(MR.strings.library_display))
|
||||||
|
libraryDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalScrollbar(
|
||||||
|
rememberScrollbarAdapter(scrollState),
|
||||||
|
Modifier.align(Alignment.CenterEnd)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TitleText(text: String) {
|
||||||
|
Box(Modifier.fillMaxWidth().padding(vertical = 8.dp), contentAlignment = Alignment.Center) {
|
||||||
|
Text(text, fontWeight = FontWeight.Bold, fontSize = 18.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ca.gosyer.jui.ui.library.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowDownward
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowUpward
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.util.fastForEach
|
||||||
|
import ca.gosyer.jui.data.library.model.Sort
|
||||||
|
import ca.gosyer.jui.ui.sources.browse.filter.SourceFilterAction
|
||||||
|
import ca.gosyer.jui.uicore.resources.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getLibrarySort(vm: LibrarySettingsViewModel): @Composable () -> Unit = remember(vm) {
|
||||||
|
@Composable {
|
||||||
|
LibrarySort(
|
||||||
|
mode = vm.sortMode.collectAsState().value,
|
||||||
|
ascending = vm.sortAscending.collectAsState().value,
|
||||||
|
setMode = {
|
||||||
|
vm.sortMode.value = it
|
||||||
|
vm.sortAscending.value = true
|
||||||
|
},
|
||||||
|
setAscending = { vm.sortAscending.value = it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LibrarySort(
|
||||||
|
mode: Sort,
|
||||||
|
ascending: Boolean,
|
||||||
|
setMode: (Sort) -> Unit,
|
||||||
|
setAscending: (Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
Column(Modifier.fillMaxWidth()) {
|
||||||
|
Sort.values().asList().fastForEach { sort ->
|
||||||
|
SourceFilterAction(
|
||||||
|
name = stringResource(sort.res),
|
||||||
|
onClick = {
|
||||||
|
if (mode == sort) {
|
||||||
|
setAscending(!ascending)
|
||||||
|
} else {
|
||||||
|
setMode(sort)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action = {
|
||||||
|
if (mode == sort) {
|
||||||
|
Icon(
|
||||||
|
imageVector = when (ascending) {
|
||||||
|
true -> Icons.Rounded.ArrowUpward
|
||||||
|
false -> Icons.Rounded.ArrowDownward
|
||||||
|
},
|
||||||
|
contentDescription = stringResource(sort.res),
|
||||||
|
modifier = Modifier.fillMaxHeight()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Box(Modifier.size(24.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -353,7 +353,7 @@ private fun SourceThinScreenContent(
|
|||||||
if (showFilterButton && !isLatest) {
|
if (showFilterButton && !isLatest) {
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
text = {
|
text = {
|
||||||
Text(stringResource(MR.strings.filter_source))
|
Text(stringResource(MR.strings.action_filter))
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
setShowingFilters(true)
|
setShowingFilters(true)
|
||||||
@@ -361,7 +361,7 @@ private fun SourceThinScreenContent(
|
|||||||
icon = {
|
icon = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Rounded.FilterList,
|
Icons.Rounded.FilterList,
|
||||||
stringResource(MR.strings.filter_source)
|
stringResource(MR.strings.action_filter)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.align(Alignment.BottomEnd)
|
modifier = Modifier.align(Alignment.BottomEnd)
|
||||||
@@ -506,7 +506,7 @@ private fun getActionItems(
|
|||||||
return listOfNotNull(
|
return listOfNotNull(
|
||||||
if (showFilterButton) {
|
if (showFilterButton) {
|
||||||
ActionItem(
|
ActionItem(
|
||||||
name = stringResource(MR.strings.filter_source),
|
name = stringResource(MR.strings.action_filter),
|
||||||
icon = Icons.Rounded.FilterList,
|
icon = Icons.Rounded.FilterList,
|
||||||
doAction = onToggleFiltersClick,
|
doAction = onToggleFiltersClick,
|
||||||
enabled = !isLatest
|
enabled = !isLatest
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -86,7 +87,7 @@ fun SourceFiltersMenu(
|
|||||||
Text(stringResource(MR.strings.reset_filters))
|
Text(stringResource(MR.strings.reset_filters))
|
||||||
}
|
}
|
||||||
Button(onSearchClicked) {
|
Button(onSearchClicked) {
|
||||||
Text(stringResource(MR.strings.filter_source))
|
Text(stringResource(MR.strings.action_filter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +142,8 @@ fun SourceFilterAction(
|
|||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().defaultMinSize(minHeight = 56.dp)
|
Modifier.fillMaxWidth().defaultMinSize(minHeight = 56.dp)
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp)
|
||||||
|
.height(IntrinsicSize.Min),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
action()
|
action()
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import ca.gosyer.jui.ui.categories.CategoriesScreenViewModel
|
|||||||
import ca.gosyer.jui.ui.downloads.DownloadsScreenViewModel
|
import ca.gosyer.jui.ui.downloads.DownloadsScreenViewModel
|
||||||
import ca.gosyer.jui.ui.extensions.ExtensionsScreenViewModel
|
import ca.gosyer.jui.ui.extensions.ExtensionsScreenViewModel
|
||||||
import ca.gosyer.jui.ui.library.LibraryScreenViewModel
|
import ca.gosyer.jui.ui.library.LibraryScreenViewModel
|
||||||
|
import ca.gosyer.jui.ui.library.settings.LibrarySettingsViewModel
|
||||||
import ca.gosyer.jui.ui.main.MainViewModel
|
import ca.gosyer.jui.ui.main.MainViewModel
|
||||||
import ca.gosyer.jui.ui.main.about.AboutViewModel
|
import ca.gosyer.jui.ui.main.about.AboutViewModel
|
||||||
import ca.gosyer.jui.ui.main.components.DebugOverlayViewModel
|
import ca.gosyer.jui.ui.main.components.DebugOverlayViewModel
|
||||||
import ca.gosyer.jui.ui.main.components.TrayViewModel
|
import ca.gosyer.jui.ui.main.components.TrayViewModel
|
||||||
|
import ca.gosyer.jui.ui.manga.MangaScreenViewModel
|
||||||
import ca.gosyer.jui.ui.reader.ReaderMenuViewModel
|
import ca.gosyer.jui.ui.reader.ReaderMenuViewModel
|
||||||
import ca.gosyer.jui.ui.settings.SettingsAdvancedViewModel
|
import ca.gosyer.jui.ui.settings.SettingsAdvancedViewModel
|
||||||
import ca.gosyer.jui.ui.settings.SettingsBackupViewModel
|
import ca.gosyer.jui.ui.settings.SettingsBackupViewModel
|
||||||
@@ -43,10 +45,11 @@ actual class ViewModelFactoryImpl(
|
|||||||
private val downloadsFactory: (Boolean) -> DownloadsScreenViewModel,
|
private val downloadsFactory: (Boolean) -> DownloadsScreenViewModel,
|
||||||
private val extensionsFactory: () -> ExtensionsScreenViewModel,
|
private val extensionsFactory: () -> ExtensionsScreenViewModel,
|
||||||
private val libraryFactory: () -> LibraryScreenViewModel,
|
private val libraryFactory: () -> LibraryScreenViewModel,
|
||||||
|
private val librarySettingsFactory: () -> LibrarySettingsViewModel,
|
||||||
private val debugOverlayFactory: () -> DebugOverlayViewModel,
|
private val debugOverlayFactory: () -> DebugOverlayViewModel,
|
||||||
private val trayFactory: () -> TrayViewModel,
|
private val trayFactory: () -> TrayViewModel,
|
||||||
private val mainFactory: () -> MainViewModel,
|
private val mainFactory: () -> MainViewModel,
|
||||||
private val mangaFactory: (params: ca.gosyer.jui.ui.manga.MangaScreenViewModel.Params) -> ca.gosyer.jui.ui.manga.MangaScreenViewModel,
|
private val mangaFactory: (params: MangaScreenViewModel.Params) -> MangaScreenViewModel,
|
||||||
private val readerFactory: (params: ReaderMenuViewModel.Params) -> ReaderMenuViewModel,
|
private val readerFactory: (params: ReaderMenuViewModel.Params) -> ReaderMenuViewModel,
|
||||||
private val settingsAdvancedFactory: () -> SettingsAdvancedViewModel,
|
private val settingsAdvancedFactory: () -> SettingsAdvancedViewModel,
|
||||||
private val themesFactory: () -> ThemesViewModel,
|
private val themesFactory: () -> ThemesViewModel,
|
||||||
@@ -73,10 +76,11 @@ actual class ViewModelFactoryImpl(
|
|||||||
DownloadsScreenViewModel::class -> downloadsFactory(arg1 as Boolean)
|
DownloadsScreenViewModel::class -> downloadsFactory(arg1 as Boolean)
|
||||||
ExtensionsScreenViewModel::class -> extensionsFactory()
|
ExtensionsScreenViewModel::class -> extensionsFactory()
|
||||||
LibraryScreenViewModel::class -> libraryFactory()
|
LibraryScreenViewModel::class -> libraryFactory()
|
||||||
|
LibrarySettingsViewModel::class -> librarySettingsFactory()
|
||||||
DebugOverlayViewModel::class -> debugOverlayFactory()
|
DebugOverlayViewModel::class -> debugOverlayFactory()
|
||||||
TrayViewModel::class -> trayFactory()
|
TrayViewModel::class -> trayFactory()
|
||||||
MainViewModel::class -> mainFactory()
|
MainViewModel::class -> mainFactory()
|
||||||
ca.gosyer.jui.ui.manga.MangaScreenViewModel::class -> mangaFactory(arg1 as ca.gosyer.jui.ui.manga.MangaScreenViewModel.Params)
|
MangaScreenViewModel::class -> mangaFactory(arg1 as MangaScreenViewModel.Params)
|
||||||
ReaderMenuViewModel::class -> readerFactory(arg1 as ReaderMenuViewModel.Params)
|
ReaderMenuViewModel::class -> readerFactory(arg1 as ReaderMenuViewModel.Params)
|
||||||
SettingsAdvancedViewModel::class -> settingsAdvancedFactory()
|
SettingsAdvancedViewModel::class -> settingsAdvancedFactory()
|
||||||
ThemesViewModel::class -> themesFactory()
|
ThemesViewModel::class -> themesFactory()
|
||||||
|
|||||||
Reference in New Issue
Block a user