mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-15 21:47:28 +01:00
Move app state banner to the very top (#8706)
This moves the banners to the root composable and so eliminates the need to track the app states in every screen.
This commit is contained in:
@@ -9,13 +9,9 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import eu.kanade.core.prefs.asState
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.presentation.components.TabbedScreen
|
||||
import eu.kanade.presentation.util.Tab
|
||||
import eu.kanade.tachiyomi.R
|
||||
@@ -25,8 +21,6 @@ import eu.kanade.tachiyomi.ui.browse.migration.sources.migrateSourceTab
|
||||
import eu.kanade.tachiyomi.ui.browse.source.sourcesTab
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
data class BrowseTab(
|
||||
private val toExtensions: Boolean = false,
|
||||
@@ -47,7 +41,6 @@ data class BrowseTab(
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val screenModel = rememberScreenModel { BrowseScreenModel() }
|
||||
|
||||
// Hoisted for extensions tab's search bar
|
||||
val extensionsScreenModel = rememberScreenModel { ExtensionsScreenModel() }
|
||||
@@ -63,8 +56,6 @@ data class BrowseTab(
|
||||
startIndex = 1.takeIf { toExtensions },
|
||||
searchQuery = extensionsQuery,
|
||||
onChangeSearchQuery = extensionsScreenModel::search,
|
||||
incognitoMode = screenModel.isIncognitoMode,
|
||||
downloadedOnlyMode = screenModel.isDownloadOnly,
|
||||
)
|
||||
|
||||
// For local source
|
||||
@@ -75,10 +66,3 @@ data class BrowseTab(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BrowseScreenModel(
|
||||
preferences: BasePreferences = Injekt.get(),
|
||||
) : ScreenModel {
|
||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
|
||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import eu.kanade.domain.source.interactor.GetRemoteManga
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
|
||||
import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.DuplicateMangaDialog
|
||||
@@ -167,8 +166,6 @@ data class BrowseSourceScreen(
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
AppStateBanners(screenModel.isDownloadOnly, screenModel.isIncognitoMode)
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
|
||||
@@ -17,7 +17,6 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.core.prefs.CheckboxState
|
||||
import eu.kanade.core.prefs.asState
|
||||
import eu.kanade.core.prefs.mapAsCheckboxState
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.category.interactor.GetCategories
|
||||
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||
import eu.kanade.domain.category.model.Category
|
||||
@@ -82,7 +81,6 @@ class BrowseSourceScreenModel(
|
||||
private val sourceId: Long,
|
||||
searchQuery: String?,
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
preferences: BasePreferences = Injekt.get(),
|
||||
sourcePreferences: SourcePreferences = Injekt.get(),
|
||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get(),
|
||||
@@ -103,9 +101,6 @@ class BrowseSourceScreenModel(
|
||||
|
||||
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
|
||||
|
||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
|
||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
|
||||
|
||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package eu.kanade.tachiyomi.ui.history
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.getValue
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.core.prefs.asState
|
||||
import eu.kanade.core.util.insertSeparators
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.chapter.model.Chapter
|
||||
import eu.kanade.domain.history.interactor.GetHistory
|
||||
import eu.kanade.domain.history.interactor.GetNextChapters
|
||||
@@ -37,15 +34,11 @@ class HistoryScreenModel(
|
||||
private val getHistory: GetHistory = Injekt.get(),
|
||||
private val getNextChapters: GetNextChapters = Injekt.get(),
|
||||
private val removeHistory: RemoveHistory = Injekt.get(),
|
||||
preferences: BasePreferences = Injekt.get(),
|
||||
) : StateScreenModel<HistoryState>(HistoryState()) {
|
||||
|
||||
private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
|
||||
val events: Flow<Event> = _events.receiveAsFlow()
|
||||
|
||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
|
||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
state.map { it.searchQuery }
|
||||
|
||||
@@ -62,8 +62,6 @@ object HistoryTab : Tab {
|
||||
HistoryScreen(
|
||||
state = state,
|
||||
snackbarHostState = snackbarHostState,
|
||||
incognitoMode = screenModel.isIncognitoMode,
|
||||
downloadedOnlyMode = screenModel.isDownloadOnly,
|
||||
onSearchQueryChange = screenModel::updateSearchQuery,
|
||||
onClickCover = { navigator.push(MangaScreen(it)) },
|
||||
onClickResume = screenModel::getNextChapterForManga,
|
||||
|
||||
@@ -89,9 +89,6 @@ class LibraryScreenModel(
|
||||
|
||||
var activeCategoryIndex: Int by libraryPreferences.lastUsedCategory().asState(coroutineScope)
|
||||
|
||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
|
||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
|
||||
|
||||
init {
|
||||
coroutineScope.launchIO {
|
||||
combine(
|
||||
|
||||
@@ -112,8 +112,6 @@ object LibraryTab : Tab {
|
||||
hasActiveFilters = state.hasActiveFilters,
|
||||
selectedCount = state.selection.size,
|
||||
title = title,
|
||||
incognitoMode = !tabVisible && screenModel.isIncognitoMode,
|
||||
downloadedOnlyMode = !tabVisible && screenModel.isDownloadOnly,
|
||||
onClickUnselectAll = screenModel::clearSelection,
|
||||
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
|
||||
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
|
||||
@@ -197,10 +195,7 @@ object LibraryTab : Tab {
|
||||
getNumberOfMangaForCategory = { state.getMangaCountForCategory(it) },
|
||||
getDisplayModeForPage = { state.categories[it].display },
|
||||
getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) },
|
||||
getLibraryForPage = { state.getLibraryItemsByPage(it) },
|
||||
isDownloadOnly = screenModel.isDownloadOnly,
|
||||
isIncognitoMode = screenModel.isIncognitoMode,
|
||||
)
|
||||
) { state.getLibraryItemsByPage(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@ import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@@ -20,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.animation.doOnEnd
|
||||
@@ -36,12 +43,14 @@ import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import cafe.adriel.voyager.transitions.ScreenTransition
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.util.Transition
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
@@ -142,47 +151,73 @@ class MainActivity : BaseActivity() {
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
setComposeContent {
|
||||
Navigator(
|
||||
screen = HomeScreen,
|
||||
disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
|
||||
) { navigator ->
|
||||
if (navigator.size == 1) {
|
||||
ConfirmExit()
|
||||
val incognito by preferences.incognitoMode().collectAsState()
|
||||
val download by preferences.downloadedOnly().collectAsState()
|
||||
Column {
|
||||
AppStateBanners(
|
||||
downloadedOnlyMode = download,
|
||||
incognitoMode = incognito,
|
||||
)
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val active = incognito || download
|
||||
val useDarkIcons = if (isSystemInDarkTheme()) active else !active
|
||||
LaunchedEffect(systemUiController, useDarkIcons) {
|
||||
systemUiController.setStatusBarColor(
|
||||
color = androidx.compose.ui.graphics.Color.Transparent,
|
||||
darkIcons = useDarkIcons,
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(navigator) {
|
||||
this@MainActivity.navigator = navigator
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// Set start screen
|
||||
handleIntentAction(intent)
|
||||
|
||||
// Reset Incognito Mode on relaunch
|
||||
preferences.incognitoMode().set(false)
|
||||
Navigator(
|
||||
screen = HomeScreen,
|
||||
disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
|
||||
) { navigator ->
|
||||
if (navigator.size == 1) {
|
||||
ConfirmExit()
|
||||
}
|
||||
}
|
||||
|
||||
// Shows current screen
|
||||
ScreenTransition(navigator = navigator, transition = { Transition.OneWayFade })
|
||||
LaunchedEffect(navigator) {
|
||||
this@MainActivity.navigator = navigator
|
||||
|
||||
// Pop source-related screens when incognito mode is turned off
|
||||
LaunchedEffect(Unit) {
|
||||
preferences.incognitoMode().changes()
|
||||
.drop(1)
|
||||
.onEach {
|
||||
if (!it) {
|
||||
val currentScreen = navigator.lastItem
|
||||
if (currentScreen is BrowseSourceScreen ||
|
||||
(currentScreen is MangaScreen && currentScreen.fromSource)
|
||||
) {
|
||||
navigator.popUntilRoot()
|
||||
if (savedInstanceState == null) {
|
||||
// Set start screen
|
||||
handleIntentAction(intent)
|
||||
|
||||
// Reset Incognito Mode on relaunch
|
||||
preferences.incognitoMode().set(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Consume insets already used by app state banners
|
||||
val boxModifier = if (incognito || download) {
|
||||
Modifier.consumeWindowInsets(WindowInsets.statusBars)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Box(modifier = boxModifier) {
|
||||
// Shows current screen
|
||||
ScreenTransition(navigator = navigator, transition = { Transition.OneWayFade })
|
||||
}
|
||||
|
||||
// Pop source-related screens when incognito mode is turned off
|
||||
LaunchedEffect(Unit) {
|
||||
preferences.incognitoMode().changes()
|
||||
.drop(1)
|
||||
.onEach {
|
||||
if (!it) {
|
||||
val currentScreen = navigator.lastItem
|
||||
if (currentScreen is BrowseSourceScreen ||
|
||||
(currentScreen is MangaScreen && currentScreen.fromSource)
|
||||
) {
|
||||
navigator.popUntilRoot()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
CheckForUpdate()
|
||||
CheckForUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
|
||||
|
||||
@@ -10,7 +10,6 @@ import eu.kanade.core.prefs.CheckboxState
|
||||
import eu.kanade.core.prefs.mapAsCheckboxState
|
||||
import eu.kanade.core.util.addOrRemove
|
||||
import eu.kanade.data.chapter.NoChaptersException
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.category.interactor.GetCategories
|
||||
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||
import eu.kanade.domain.category.model.Category
|
||||
@@ -53,7 +52,6 @@ import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
@@ -64,7 +62,6 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
@@ -81,7 +78,6 @@ class MangaInfoScreenModel(
|
||||
val context: Context,
|
||||
val mangaId: Long,
|
||||
private val isFromSource: Boolean,
|
||||
basePreferences: BasePreferences = Injekt.get(),
|
||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||
private val uiPreferences: UiPreferences = Injekt.get(),
|
||||
@@ -130,17 +126,6 @@ class MangaInfoScreenModel(
|
||||
mutableState.update { if (it is MangaScreenState.Success) func(it) else it }
|
||||
}
|
||||
|
||||
private var incognitoMode = false
|
||||
set(value) {
|
||||
updateSuccessState { it.copy(isIncognitoMode = value) }
|
||||
field = value
|
||||
}
|
||||
private var downloadedOnlyMode = false
|
||||
set(value) {
|
||||
updateSuccessState { it.copy(isDownloadedOnlyMode = value) }
|
||||
field = value
|
||||
}
|
||||
|
||||
init {
|
||||
val toChapterItemsParams: List<Chapter>.(manga: Manga) -> List<ChapterItem> = { manga ->
|
||||
toChapterItems(
|
||||
@@ -189,8 +174,6 @@ class MangaInfoScreenModel(
|
||||
isFromSource = isFromSource,
|
||||
chapters = chapters,
|
||||
isRefreshingData = needRefreshInfo || needRefreshChapter,
|
||||
isIncognitoMode = incognitoMode,
|
||||
isDownloadedOnlyMode = downloadedOnlyMode,
|
||||
dialog = null,
|
||||
)
|
||||
}
|
||||
@@ -210,14 +193,6 @@ class MangaInfoScreenModel(
|
||||
// Initial loading finished
|
||||
updateSuccessState { it.copy(isRefreshingData = false) }
|
||||
}
|
||||
|
||||
basePreferences.incognitoMode()
|
||||
.asHotFlow { incognitoMode = it }
|
||||
.launchIn(coroutineScope)
|
||||
|
||||
basePreferences.downloadedOnly()
|
||||
.asHotFlow { downloadedOnlyMode = it }
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
fun fetchAllFromSource(manualFetch: Boolean = true) {
|
||||
@@ -1037,8 +1012,6 @@ sealed class MangaScreenState {
|
||||
val chapters: List<ChapterItem>,
|
||||
val trackItems: List<TrackItem> = emptyList(),
|
||||
val isRefreshingData: Boolean = false,
|
||||
val isIncognitoMode: Boolean = false,
|
||||
val isDownloadedOnlyMode: Boolean = false,
|
||||
val dialog: MangaInfoScreenModel.Dialog? = null,
|
||||
) : MangaScreenState() {
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.core.prefs.asState
|
||||
import eu.kanade.core.util.addOrRemove
|
||||
import eu.kanade.core.util.insertSeparators
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
||||
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
||||
@@ -62,16 +61,12 @@ class UpdatesScreenModel(
|
||||
private val getChapter: GetChapter = Injekt.get(),
|
||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||
basePreferences: BasePreferences = Injekt.get(),
|
||||
uiPreferences: UiPreferences = Injekt.get(),
|
||||
) : StateScreenModel<UpdatesState>(UpdatesState()) {
|
||||
|
||||
private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
|
||||
val events: Flow<Event> = _events.receiveAsFlow()
|
||||
|
||||
val isDownloadOnly: Boolean by basePreferences.downloadedOnly().asState(coroutineScope)
|
||||
val isIncognitoMode: Boolean by basePreferences.incognitoMode().asState(coroutineScope)
|
||||
|
||||
val lastUpdated by libraryPreferences.libraryUpdateLastTimestamp().asState(coroutineScope)
|
||||
|
||||
val relativeTime: Int by uiPreferences.relativeTime().asState(coroutineScope)
|
||||
|
||||
@@ -56,8 +56,6 @@ object UpdatesTab : Tab {
|
||||
UpdateScreen(
|
||||
state = state,
|
||||
snackbarHostState = screenModel.snackbarHostState,
|
||||
incognitoMode = screenModel.isIncognitoMode,
|
||||
downloadedOnlyMode = screenModel.isDownloadOnly,
|
||||
lastUpdated = screenModel.lastUpdated,
|
||||
relativeTime = screenModel.relativeTime,
|
||||
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },
|
||||
|
||||
Reference in New Issue
Block a user