Navigation.kt: Type-safety, non-null parameters for Screens, improved navigation + Included generated Room Database schemas to be able to build the project.

This commit is contained in:
acclorite 2024-06-19 18:54:17 +03:00
parent be9630ca24
commit 667a91f950
53 changed files with 922 additions and 465 deletions

View file

@ -112,11 +112,15 @@ class Activity : AppCompatActivity() {
themeContrast = state.value.themeContrast!!
) {
NavigationHost(
startScreen = if (state.value.showStartScreen!!) Screen.START
else Screen.LIBRARY
startScreen = if (state.value.showStartScreen!!) Screen.Start
else Screen.Library
) {
navigation(
screens = arrayOf(Screen.LIBRARY, Screen.HISTORY, Screen.BROWSE),
screens = arrayOf(
Screen.Library.getRoute(),
Screen.History.getRoute(),
Screen.Browse.getRoute()
),
bottomBar = {
BottomNavigationBar()
},
@ -125,8 +129,7 @@ class Activity : AppCompatActivity() {
}
) {
// Library
composable(
screen = Screen.LIBRARY,
composable<Screen.Library>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
@ -134,8 +137,7 @@ class Activity : AppCompatActivity() {
}
// History
composable(
screen = Screen.HISTORY,
composable<Screen.History>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
@ -143,8 +145,7 @@ class Activity : AppCompatActivity() {
}
// Browse
composable(
screen = Screen.BROWSE,
composable<Screen.Browse>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
@ -153,57 +154,54 @@ class Activity : AppCompatActivity() {
}
// Book Info
composable(screen = Screen.BOOK_INFO) {
BookInfoScreenRoot()
composable<Screen.BookInfo> {
BookInfoScreenRoot(it)
}
// Reader
composable(screen = Screen.READER) {
ReaderScreenRoot()
composable<Screen.Reader> {
ReaderScreenRoot(it)
}
// Settings
composable(screen = Screen.SETTINGS) {
composable<Screen.Settings> {
SettingsScreenRoot()
}
// Nested settings categories
composable(screen = Screen.GENERAL_SETTINGS) {
composable<Screen.Settings.General> {
GeneralSettingsRoot()
}
composable(screen = Screen.APPEARANCE_SETTINGS) {
composable<Screen.Settings.Appearance> {
AppearanceSettingsRoot()
}
composable(screen = Screen.READER_SETTINGS) {
composable<Screen.Settings.ReaderSettings> {
ReaderSettingsRoot()
}
// About screen
composable(screen = Screen.ABOUT) {
composable<Screen.About> {
AboutScreenRoot()
}
// Nested about categories
composable(screen = Screen.LICENSES) {
composable<Screen.About.Licenses> {
LicensesScreenRoot()
}
composable(screen = Screen.LICENSES_INFO) {
LicenseInfoScreenRoot()
composable<Screen.About.LicenseInfo> {
LicenseInfoScreenRoot(it)
}
composable(screen = Screen.CREDITS) {
composable<Screen.About.Credits> {
CreditsScreenRoot()
}
// Help screen
composable(screen = Screen.HELP) {
HelpScreenRoot()
composable<Screen.Help> {
HelpScreenRoot(it)
}
// Start screen
composable(
screen = Screen.START,
enterAnim = Transitions.FadeTransitionIn
) {
composable<Screen.Start>(enterAnim = Transitions.FadeTransitionIn) {
StartScreenRoot()
}
}

View file

@ -30,21 +30,21 @@ object Constants {
// Navigation items for NavigationBars.
val NAVIGATION_ITEMS = listOf(
NavigationItem(
screen = Screen.LIBRARY,
screen = Screen.Library,
title = R.string.library_screen,
tooltip = R.string.library_content_desc,
selectedIcon = R.drawable.library_screen_filled,
unselectedIcon = R.drawable.library_screen_outlined
),
NavigationItem(
screen = Screen.HISTORY,
screen = Screen.History,
title = R.string.history_screen,
tooltip = R.string.history_content_desc,
selectedIcon = R.drawable.history_screen_filled,
unselectedIcon = R.drawable.history_screen_outlined
),
NavigationItem(
screen = Screen.BROWSE,
screen = Screen.Browse,
title = R.string.browse_screen,
tooltip = R.string.browse_content_desc,
selectedIcon = R.drawable.browse_screen_filled,

View file

@ -1,8 +1,11 @@
package ua.acclorite.book_story.domain.util
import android.graphics.Bitmap
import ua.acclorite.book_story.presentation.data.Navigator
typealias CoverImage = Bitmap
typealias Selected = Boolean
typealias ID = Int
typealias LanguageCode = String
typealias Route = String
typealias OnNavigate = (Navigator.() -> Unit) -> Unit

View file

@ -4,13 +4,18 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.runtime.Composable
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
/**
* Go back arrow button. Prevents double or triple clicking on it while going back action is performed.
* Go back arrow button.
* Prevents double or triple clicking on it while going back action is performed.
*
* @param onNavigate [OnNavigate].
* @param enabled Whether this button is enabled.
* @param customOnClick Call before going back.
*/
@Composable
fun GoBackButton(navigator: Navigator, enabled: Boolean = true, customOnClick: () -> Unit = {}) {
fun GoBackButton(onNavigate: OnNavigate, enabled: Boolean = true, customOnClick: () -> Unit = {}) {
CustomIconButton(
icon = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = R.string.go_back_content_desc,
@ -18,6 +23,8 @@ fun GoBackButton(navigator: Navigator, enabled: Boolean = true, customOnClick: (
enabled = enabled
) {
customOnClick()
navigator.navigateBack()
onNavigate {
navigateBack()
}
}
}

View file

@ -21,7 +21,8 @@ import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Screen
/**
* More drop down. Navigates to Settings, About and Help screens.
* More drop down.
* Navigates to Settings, About and Help screens.
*/
@Composable
fun MoreDropDown() {
@ -55,7 +56,7 @@ fun MoreDropDown() {
)
},
onClick = {
navigator.navigate(Screen.SETTINGS, false)
navigator.navigate(Screen.Settings)
},
contentPadding = PaddingValues(start = startPadding, end = endPadding)
)
@ -68,7 +69,7 @@ fun MoreDropDown() {
)
},
onClick = {
navigator.navigate(Screen.HELP, false)
navigator.navigate(Screen.Help(false))
},
contentPadding = PaddingValues(start = startPadding, end = endPadding)
)
@ -81,7 +82,7 @@ fun MoreDropDown() {
)
},
onClick = {
navigator.navigate(Screen.ABOUT, false)
navigator.navigate(Screen.About)
},
contentPadding = PaddingValues(start = startPadding, end = endPadding)
)

View file

@ -8,21 +8,25 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.Route
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Screen
/**
* Bottom navigation bar, uses default [NavigationBar].
*/
@Composable
fun BottomNavigationBar() {
var currentScreen: Screen? by remember { mutableStateOf(null) }
var currentScreen: Route? by remember { mutableStateOf(null) }
val navigator = LocalNavigator.current
LaunchedEffect(Unit) {
navigator.getCurrentScreen().collect {
if (it == Screen.LIBRARY || it == Screen.HISTORY || it == Screen.BROWSE) {
currentScreen = it
navigator.currentScreen.collect { route ->
if (
Constants.NAVIGATION_ITEMS.any {
navigator.run { it.screen.getRoute() } == route
}
) {
currentScreen = route
}
}
}
@ -31,7 +35,7 @@ fun BottomNavigationBar() {
Constants.NAVIGATION_ITEMS.forEach {
BottomNavigationBarItem(
item = it,
isSelected = currentScreen == it.screen
isSelected = currentScreen == navigator.run { it.screen.getRoute() }
) {
navigator.navigate(it.screen, false)
}

View file

@ -24,22 +24,26 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.Route
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Screen
/**
* Custom Navigation Rail. It is used to be shown on Tablets.
*/
@Composable
fun BoxScope.CustomNavigationRail() {
var currentScreen: Screen? by remember { mutableStateOf(null) }
var currentScreen: Route? by remember { mutableStateOf(null) }
val navigator = LocalNavigator.current
val layoutDirection = LocalLayoutDirection.current
LaunchedEffect(Unit) {
navigator.getCurrentScreen().collect {
if (it == Screen.LIBRARY || it == Screen.HISTORY || it == Screen.BROWSE) {
currentScreen = it
navigator.currentScreen.collect { route ->
if (
Constants.NAVIGATION_ITEMS.any {
navigator.run { it.screen.getRoute() } == route
}
) {
currentScreen = route
}
}
}
@ -69,7 +73,7 @@ fun BoxScope.CustomNavigationRail() {
Constants.NAVIGATION_ITEMS.forEach {
CustomNavigationRailItem(
item = it,
isSelected = currentScreen == it.screen
isSelected = currentScreen == navigator.run { it.screen.getRoute() }
) {
navigator.navigate(it.screen, false)
}

View file

@ -43,60 +43,87 @@ import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.lifecycle.withCreationCallback
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import ua.acclorite.book_story.domain.util.Route
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.ui.Transitions
import java.io.Serializable
private const val CURRENT_SCREEN = "current_screen"
private const val BACKSTACK = "back_stack"
private const val USE_BACK_ANIM = "use_back_animation"
private const val ARGUMENTS = "arguments"
private const val SCREENS = "screens"
/**
* Passed in [CompositionLocalProvider] and can be accessed through [LocalNavigator].current.
*/
val LocalNavigator = compositionLocalOf<Navigator> {
error("Cannot initialize Navigator.")
}
/**
* All screens are listed here, later each screen will be passed as a param for [Navigator.composable] function.
*/
enum class Screen {
LIBRARY,
HISTORY,
BROWSE,
BOOK_INFO,
READER,
SETTINGS,
GENERAL_SETTINGS,
APPEARANCE_SETTINGS,
READER_SETTINGS,
ABOUT,
LICENSES,
LICENSES_INFO,
CREDITS,
HELP,
START
}
/**
* Navigation Argument.
* Screens. Should be [Serializable] to be able to pass them inside [SavedStateHandle].
* Each screen should have unique name.
*/
@Immutable
@Parcelize
data class Argument<out T : Serializable>(
val key: String,
val value: T
) : Parcelable
sealed class Screen : Parcelable {
@Parcelize
data object Library : Screen()
@Parcelize
data object History : Screen()
@Parcelize
data object Browse : Screen()
@Parcelize
data class BookInfo(
val bookId: Int
) : Screen()
@Parcelize
data class Reader(
val bookId: Int
) : Screen()
@Parcelize
data object Settings : Screen() {
@Parcelize
data object General : Screen()
@Parcelize
data object Appearance : Screen()
@Parcelize
data object ReaderSettings : Screen()
}
@Parcelize
data object About : Screen() {
@Parcelize
data object Licenses : Screen()
@Parcelize
data class LicenseInfo(val licenseId: String) : Screen()
@Parcelize
data object Credits : Screen()
}
@Parcelize
data class Help(
val fromStart: Boolean
) : Screen()
@Parcelize
data object Start : Screen()
}
/**
* Navigator. Using to navigate between screens.
* Navigator. Used to navigate between screens.
*
* [Navigator.currentScreen] param represents current [Screen].
* [Navigator.navigate] navigates to [Screen] passed as param.
@ -104,139 +131,186 @@ data class Argument<out T : Serializable>(
@HiltViewModel(assistedFactory = Navigator.Factory::class)
class Navigator @AssistedInject constructor(
private val savedStateHandle: SavedStateHandle,
@Assisted startScreen: Screen
@Assisted private val startScreen: Screen
) : ViewModel() {
private val currentScreen = savedStateHandle.getStateFlow(CURRENT_SCREEN, startScreen)
private val useBackAnimation = savedStateHandle.getStateFlow(USE_BACK_ANIM, false)
private val backStack = savedStateHandle.getStateFlow(BACKSTACK, mutableListOf<Screen>())
private val arguments = savedStateHandle
.getStateFlow(ARGUMENTS, mutableListOf<Argument<Serializable>>())
val currentScreen = savedStateHandle.getStateFlow(CURRENT_SCREEN, startScreen.getRoute())
val useBackAnimation = savedStateHandle.getStateFlow(USE_BACK_ANIM, false)
private val backStack = savedStateHandle.getStateFlow(BACKSTACK, mutableListOf<Route>())
val screens = savedStateHandle.getStateFlow(SCREENS, mutableListOf<Screen>())
fun putArgument(argument: Argument<Serializable>) {
init {
putScreen(startScreen)
}
/**
* Saves screen into [screens]. Later can be retrieved via [retrieveScreen].
*
* @param screen [Screen].
*/
fun putScreen(screen: Screen) {
var found = false
for ((index, arg) in arguments.value.withIndex()) {
if (arg.key == argument.key) {
arguments.value[index] = argument
for ((index, arg) in screens.value.withIndex()) {
if (arg.getRoute() == screen.getRoute()) {
screens.value[index] = screen
found = true
break
}
}
if (!found) {
arguments.value.add(argument)
screens.value.add(screen)
}
}
fun retrieveArgument(key: String): Serializable? {
for (arg in arguments.value) {
if (arg.key == key) {
return arg.value
/**
* Retrieves screen that was put via [putScreen].
*
* @exception Exception if there is no such screen saved that specified in [S], throws an [Exception].
*/
inline fun <reified S : Screen> retrieveScreen(): S {
for (arg in screens.value) {
if (arg.getRoute() == getRoute<S>()) {
return arg as S
}
}
return null
throw Exception("Screen was not found.")
}
fun clearArgument(key: String) {
arguments.value.removeIf { it.key == key }
}
fun navigate(screen: Screen, useBackAnimation: Boolean, vararg args: Argument<Serializable>) =
viewModelScope.launch(Dispatchers.Default) {
backStack.value.add(currentScreen.value)
args.forEach {
putArgument(it)
}
savedStateHandle[USE_BACK_ANIM] = useBackAnimation
savedStateHandle[CURRENT_SCREEN] = screen
}
fun navigateWithoutBackStack(
/**
* Navigates to the desired screen. Ignored if [currentScreen] is already [screen].
*
* @param screen [Screen] to navigate to.
* @param useBackAnimation Whether back animation should be used(as when user goes back).
* @param saveInBackStack Whether this screen should be saved in [backStack] (basically history of all opened screens).
*/
fun navigate(
screen: Screen,
useBackAnimation: Boolean,
vararg args: Argument<Serializable>
useBackAnimation: Boolean = false,
saveInBackStack: Boolean = true
) = viewModelScope.launch(Dispatchers.Default) {
if (backStack.value.lastOrNull() == screen) {
backStack.value.removeLast()
if (screen.getRoute() == currentScreen.value) {
return@launch
}
args.forEach {
putArgument(it)
if (saveInBackStack) {
backStack.value.add(currentScreen.value)
}
putScreen(screen)
savedStateHandle[USE_BACK_ANIM] = useBackAnimation
savedStateHandle[CURRENT_SCREEN] = screen
savedStateHandle[CURRENT_SCREEN] = screen.getRoute()
}
fun navigateBack(useBackAnimation: Boolean = true) =
viewModelScope.launch(Dispatchers.Default) {
if (canGoBack()) {
savedStateHandle[USE_BACK_ANIM] = useBackAnimation
savedStateHandle[CURRENT_SCREEN] = backStack.value.last()
backStack.value.removeLast()
}
}
/**
* Navigates user to the previous screen, if there is.
* If there is nowhere to go, this call is ignored.
*
* @param useBackAnimation Whether user should see back animation when he goes to the previous screen.
*/
fun navigateBack(
useBackAnimation: Boolean = true
) = viewModelScope.launch(Dispatchers.Default) {
if (canGoBack()) {
savedStateHandle[USE_BACK_ANIM] = useBackAnimation
savedStateHandle[CURRENT_SCREEN] = backStack.value.last()
backStack.value.removeLast()
}
}
/**
* Clears the whole [backStack].
*/
fun clearBackStack() {
backStack.value.clear()
}
/**
* Whether there is screen to go back.
*/
fun canGoBack(): Boolean {
return backStack.value.isNotEmpty()
}
fun getCurrentScreen(): StateFlow<Screen> {
return currentScreen
}
fun getUseBackAnim(): StateFlow<Boolean> {
return useBackAnimation
/**
* Gets route of specified [S].
*/
inline fun <reified S : Screen> getRoute(): Route {
return S::class.simpleName!!
}
/**
* Animated Screen. Used in [NavigationHost]. Be sure to not use the same [screen] parameter twice, it'll override the highest one in your code.
* Gets route of this screen.
*/
fun Screen.getRoute(): Route {
return this::class.simpleName!!
}
/**
* Animated Screen. Used in [NavigationHost].
* Each [composable] should have unique [Screen].
*
* @param screen The [Screen] that represents [content].
* @param enterAnim Enter Animation.
* @param backEnterAnim Enter Animation for navigating back.
* @param exitAnim Exit Animation.
* @param backExitAnim Exit Animation for navigating back.
* @param content The Screen content to show when [Navigator.currentScreen] equals [screen].
* @param content The Screen content to show when [Navigator.currentScreen] equals [S].
*/
@SuppressLint("ComposableNaming")
@Composable
fun composable(
screen: Screen,
inline fun <reified S : Screen> composable(
enterAnim: EnterTransition = Transitions.SlidingTransitionIn,
backEnterAnim: EnterTransition = Transitions.BackSlidingTransitionIn,
exitAnim: ExitTransition = Transitions.SlidingTransitionOut,
backExitAnim: ExitTransition = Transitions.BackSlidingTransitionOut,
content: @Composable () -> Unit
noinline content: @Composable (screen: S) -> Unit
) {
val currentRoute by currentScreen.collectAsState()
val useBackAnimation by useBackAnimation.collectAsState()
CustomAnimatedVisibility(
visible = getCurrentScreen().collectAsState().value == screen,
enter = if (!useBackAnimation.collectAsState().value) enterAnim else backEnterAnim,
exit = if (!useBackAnimation.collectAsState().value) exitAnim else backExitAnim
visible = currentRoute == getRoute<S>(),
enter = if (!useBackAnimation) enterAnim else backEnterAnim,
exit = if (!useBackAnimation) exitAnim else backExitAnim
) {
content()
val screen = remember { retrieveScreen<S>() }
content(screen)
}
}
/**
* Navigator's navigation.
* Shows BottomBar or Navigation Rail when any of the [screens] are currently showing.
*
* @param screens [Screen]s where navigation shows.
* @param enterBarAnim Enter animation for navigation.
* @param backEnterBarAnim Back enter animation for navigation.
* @param exitBarAnim Exit animation for navigation.
* @param backExitBarAnim Back exit animation for navigation.
* @param bottomBar Bottom bar, sticks to the bottom.
* @param navigationRail Navigation rail, sticks to the sides.
* @param content Content, all [screens] should be inside as [composable].
*/
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@SuppressLint("ComposableNaming")
@Composable
fun navigation(
vararg screens: Screen,
vararg screens: Route,
enterBarAnim: EnterTransition = Transitions.SlidingTransitionIn,
backEnterBarAnim: EnterTransition = Transitions.BackSlidingTransitionIn,
exitBarAnim: ExitTransition = Transitions.SlidingTransitionOut,
backExitBarAnim: ExitTransition = Transitions.BackSlidingTransitionOut,
bottomBar: @Composable () -> Unit,
navigationRail: @Composable BoxScope.() -> Unit,
content: @Composable () -> Unit
) {
val activity = LocalContext.current as ComponentActivity
val currentScreen by getCurrentScreen().collectAsState()
val useBackAnimation by getUseBackAnim().collectAsState()
val currentScreen by currentScreen.collectAsState()
val useBackAnimation by useBackAnimation.collectAsState()
val shouldShow by remember(currentScreen) {
derivedStateOf {
screens.any { it == currentScreen }
@ -251,10 +325,10 @@ class Navigator @AssistedInject constructor(
CustomAnimatedVisibility(
visible = shouldShow,
enter = if (useBackAnimation) Transitions.BackSlidingTransitionIn
else Transitions.SlidingTransitionIn,
exit = if (useBackAnimation) Transitions.BackSlidingTransitionOut
else Transitions.SlidingTransitionOut
enter = if (useBackAnimation) backEnterBarAnim
else enterBarAnim,
exit = if (useBackAnimation) backExitBarAnim
else exitBarAnim
) {
Scaffold(
bottomBar = {
@ -305,13 +379,13 @@ class Navigator @AssistedInject constructor(
}
/**
* Navigation Host. Contains [Navigator.composable]s in [content].
* Custom Navigation Host. Contains [Navigator.composable]s in [content].
* Based on [CustomAnimatedVisibility].
*
* @param startScreen Start Screen. Be sure to pass [Screen] that uses in one of your [Navigator.composable]s.
* @param colorBetweenAnimations The color, that using between animations, recommended to set this to background or navigation bar color.
* @param content Content of the [NavigationHost]. Highly recommended to use [Navigator.composable].
*/
@Composable
fun NavigationHost(
startScreen: Screen,

View file

@ -33,10 +33,10 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.about.components.AboutItem
import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog
@ -53,7 +53,7 @@ fun AboutScreenRoot() {
AboutScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = aboutViewModel::onEvent
)
}
@ -62,7 +62,7 @@ fun AboutScreenRoot() {
@Composable
private fun AboutScreen(
state: State<AboutState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (AboutEvent) -> Unit
) {
val context = LocalContext.current
@ -87,7 +87,7 @@ private fun AboutScreen(
Text(stringResource(id = R.string.about_screen))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(
@ -213,7 +213,9 @@ private fun AboutScreen(
title = stringResource(id = R.string.licenses_option),
description = null
) {
navigator.navigate(Screen.LICENSES, false)
onNavigate {
navigate(Screen.About.Licenses)
}
}
}
@ -222,7 +224,9 @@ private fun AboutScreen(
title = stringResource(id = R.string.credits_option),
description = null
) {
navigator.navigate(Screen.CREDITS, false)
onNavigate {
navigate(Screen.About.Credits)
}
}
}

View file

@ -21,11 +21,11 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
import ua.acclorite.book_story.presentation.screens.about.nested.credits.components.CreditItem
@ -36,7 +36,7 @@ fun CreditsScreenRoot() {
val aboutViewModel: AboutViewModel = hiltViewModel()
CreditsScreen(
navigator = navigator,
onNavigate = { navigator.it() },
onAboutNavigateEvent = aboutViewModel::onEvent
)
}
@ -44,7 +44,7 @@ fun CreditsScreenRoot() {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CreditsScreen(
navigator: Navigator,
onNavigate: OnNavigate,
onAboutNavigateEvent: (AboutEvent.OnNavigateToBrowserPage) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
@ -62,7 +62,7 @@ private fun CreditsScreen(
Text(stringResource(id = R.string.credits_option))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(

View file

@ -42,19 +42,20 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoEvent
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoState
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoViewModel
import ua.acclorite.book_story.presentation.ui.SlidingTransition
@Composable
fun LicenseInfoScreenRoot() {
fun LicenseInfoScreenRoot(screen: Screen.About.LicenseInfo) {
val navigator = LocalNavigator.current
val context = LocalContext.current
val licenseInfoViewModel: LicenseInfoViewModel = hiltViewModel()
@ -63,15 +64,16 @@ fun LicenseInfoScreenRoot() {
LaunchedEffect(Unit) {
licenseInfoViewModel.init(
navigator,
context
screen = screen,
onNavigate = { navigator.it() },
context = context
)
}
LicenseInfoScreen(
state = state,
onEvent = licenseInfoViewModel::onEvent,
navigator = navigator
onNavigate = { navigator.it() },
onEvent = licenseInfoViewModel::onEvent
)
}
@ -79,8 +81,8 @@ fun LicenseInfoScreenRoot() {
@Composable
private fun LicenseInfoScreen(
state: State<LicenseInfoState>,
onEvent: (LicenseInfoEvent) -> Unit,
navigator: Navigator
onNavigate: OnNavigate,
onEvent: (LicenseInfoEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
val context = LocalContext.current
@ -106,7 +108,7 @@ private fun LicenseInfoScreen(
)
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
actions = {
if (state.value.license?.website?.isNotBlank() == true) {

View file

@ -13,7 +13,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject
@HiltViewModel
@ -42,25 +43,20 @@ class LicenseInfoViewModel @Inject constructor() : ViewModel() {
}
}
fun init(navigator: Navigator, context: Context) {
fun init(screen: Screen.About.LicenseInfo, onNavigate: OnNavigate, context: Context) {
viewModelScope.launch(Dispatchers.IO) {
_state.update {
it.copy(license = null)
}
val licenseId = navigator.retrieveArgument("license") as? String
if (licenseId == null) {
navigator.navigateBack()
return@launch
}
val license = Libs.Builder().withContext(context).build().libraries.find {
it.uniqueId == licenseId
it.uniqueId == screen.licenseId
}
if (license == null) {
navigator.navigateBack()
onNavigate {
navigateBack()
}
return@launch
}

View file

@ -28,13 +28,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.components.LicenseItem
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesState
@ -54,7 +53,7 @@ fun LicensesScreenRoot() {
LicensesScreen(
state = state,
navigator = navigator
onNavigate = { navigator.it() }
)
}
@ -62,7 +61,7 @@ fun LicensesScreenRoot() {
@Composable
private fun LicensesScreen(
state: State<LicensesState>,
navigator: Navigator
onNavigate: OnNavigate
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
@ -78,7 +77,7 @@ private fun LicensesScreen(
Text(stringResource(id = R.string.licenses_option))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(
@ -105,11 +104,9 @@ private fun LicensesScreen(
customItems(state.value.licenses, key = { it.uniqueId }) {
LicenseItem(library = it) {
navigator.navigate(
Screen.LICENSES_INFO,
false,
Argument("license", it.uniqueId)
)
onNavigate {
navigate(Screen.About.LicenseInfo(it.uniqueId))
}
}
}

View file

@ -57,13 +57,14 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomSnackbar
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoBackground
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoDescriptionSection
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoInfoSection
@ -87,7 +88,7 @@ import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions
@Composable
fun BookInfoScreenRoot() {
fun BookInfoScreenRoot(screen: Screen.BookInfo) {
val navigator = LocalNavigator.current
val viewModel: BookInfoViewModel = hiltViewModel()
@ -99,13 +100,14 @@ fun BookInfoScreenRoot() {
LaunchedEffect(Unit) {
viewModel.init(
navigator
screen = screen,
onNavigate = { navigator.it() }
)
}
BookInfoScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
@ -117,7 +119,7 @@ fun BookInfoScreenRoot() {
@Composable
private fun BookInfoScreen(
state: State<BookInfoState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (BookInfoEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit,
@ -203,7 +205,7 @@ private fun BookInfoScreen(
onEvent(BookInfoEvent.OnShowHideEditTitle)
}
} else {
GoBackButton(navigator = navigator, enabled = !state.value.isRefreshing) {
GoBackButton(onNavigate = onNavigate, enabled = !state.value.isRefreshing) {
onEvent(BookInfoEvent.OnCancelUpdate)
}
}
@ -329,7 +331,7 @@ private fun BookInfoScreen(
FloatingActionButton(
onClick = {
if (!state.value.isRefreshing) {
onEvent(BookInfoEvent.OnNavigateToReaderScreen(navigator))
onEvent(BookInfoEvent.OnNavigateToReaderScreen(onNavigate = onNavigate))
}
},
shape = MaterialTheme.shapes.large,
@ -387,7 +389,9 @@ private fun BookInfoScreen(
if (!state.value.isRefreshing) {
onEvent(BookInfoEvent.OnCancelUpdate)
navigator.navigateBack()
onNavigate {
navigateBack()
}
}
}
}

View file

@ -16,6 +16,11 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/**
* Delete dialog. Deletes current book.
*
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryLoadEvent [LibraryEvent] callback.
* @param onHistoryLoadEvent [HistoryEvent] callback.
* @param onBrowseLoadEvent [BrowseEvent] callback.
*/
@Composable
fun BookInfoDeleteDialog(
@ -40,7 +45,7 @@ fun BookInfoDeleteDialog(
onAction = {
onEvent(
BookInfoEvent.OnDeleteBook(
navigator = navigator,
onNavigate = { navigator.it() },
refreshList = {
onLibraryLoadEvent(LibraryEvent.OnLoadList)
onBrowseLoadEvent(BrowseEvent.OnLoadList)

View file

@ -21,7 +21,13 @@ import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/**
* Move dialog. Moves current book to the selected category.
* Move dialog.
* Moves current book to the selected category.
*
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/
@Composable
fun BookInfoMoveDialog(
@ -60,7 +66,7 @@ fun BookInfoMoveDialog(
updatePage = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it))
},
navigator = navigator
onNavigate = { navigator.it() }
)
)
Toast.makeText(

View file

@ -7,7 +7,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.focus.FocusRequester
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
@Immutable
sealed class BookInfoEvent {
@ -23,11 +23,13 @@ sealed class BookInfoEvent {
data class OnUpdateTitle(val refreshList: (Book) -> Unit) : BookInfoEvent()
data object OnShowHideDeleteDialog : BookInfoEvent()
data object OnShowHideMoveDialog : BookInfoEvent()
data class OnDeleteBook(val refreshList: () -> Unit, val navigator: Navigator) : BookInfoEvent()
data class OnDeleteBook(val refreshList: () -> Unit, val onNavigate: OnNavigate) :
BookInfoEvent()
data class OnMoveBook(
val refreshList: (Book) -> Unit,
val updatePage: (Int) -> Unit,
val navigator: Navigator
val onNavigate: OnNavigate
) : BookInfoEvent()
data class OnSelectCategory(val category: Category) : BookInfoEvent()
@ -51,7 +53,7 @@ sealed class BookInfoEvent {
) : BookInfoEvent()
data class OnNavigateToReaderScreen(
val navigator: Navigator
val onNavigate: OnNavigate
) : BookInfoEvent()
data object OnDismissConfirmUpdateDialog : BookInfoEvent()

View file

@ -22,15 +22,14 @@ import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.use_case.DeleteBooks
import ua.acclorite.book_story.domain.use_case.GetBookById
import ua.acclorite.book_story.domain.use_case.GetBookFromFile
import ua.acclorite.book_story.domain.use_case.GetBooksById
import ua.acclorite.book_story.domain.use_case.GetText
import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.use_case.UpdateBookWithText
import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.domain.use_case.UpdateCoverImageOfBook
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import java.io.File
import java.util.Date
@ -45,7 +44,7 @@ class BookInfoViewModel @Inject constructor(
private val insertHistory: InsertHistory,
private val deleteBooks: DeleteBooks,
private val getBookFromFile: GetBookFromFile,
private val getBookById: GetBooksById,
private val getBookById: GetBookById,
private val getText: GetText
) : ViewModel() {
@ -76,11 +75,8 @@ class BookInfoViewModel @Inject constructor(
image
)
val newCoverImage = getBookById.execute(
listOf(
_state.value.book.id
)
).first().coverImage
val newCoverImage =
getBookById.execute(_state.value.book.id)?.coverImage ?: return@launch
_state.update {
it.copy(
@ -197,7 +193,9 @@ class BookInfoViewModel @Inject constructor(
deleteBooks.execute(listOf(_state.value.book))
event.refreshList()
event.navigator.navigateBack()
event.onNavigate {
navigateBack()
}
}
}
@ -240,7 +238,12 @@ class BookInfoViewModel @Inject constructor(
it != _state.value.selectedCategory
}.size - 1
)
event.navigator.navigate(Screen.LIBRARY, true)
event.onNavigate {
navigate(
Screen.Library,
useBackAnimation = true
)
}
}
}
@ -553,9 +556,35 @@ class BookInfoViewModel @Inject constructor(
}
if (_state.value.textChanged) {
val newBook = getBookById.execute(
listOf(_state.value.book.id)
).first()
val newBook = getBookById.execute(_state.value.book.id)
if (newBook == null) {
onEvent(
BookInfoEvent.OnShowSnackbar(
text = event.context.getString(
R.string.error_something_went_wrong_with_file
),
action = event.context.getString(R.string.retry),
onAction = {
onEvent(
BookInfoEvent.OnLoadUpdate(
snackbarState = event.snackbarState,
context = event.context
)
)
},
durationMillis = 4000L,
snackbarState = event.snackbarState
)
)
delay(500)
_state.update {
it.copy(
isRefreshing = false
)
}
return@launch
}
_state.update {
it.copy(
@ -611,36 +640,29 @@ class BookInfoViewModel @Inject constructor(
)
)
}
event.navigator.navigate(
Screen.READER,
false,
Argument(
"book", _state.value.book.id
event.onNavigate {
navigate(
Screen.Reader(_state.value.book.id)
)
)
}
}
}
}
}
fun init(navigator: Navigator) {
fun init(screen: Screen.BookInfo, onNavigate: OnNavigate) {
viewModelScope.launch {
val bookId = navigator.retrieveArgument("book") as? Int
val book = getBookById.execute(screen.bookId)
if (bookId == null) {
navigator.navigateBack()
return@launch
}
val book = getBookById.execute(listOf(bookId))
if (book.isEmpty()) {
navigator.navigateBack()
if (book == null) {
onNavigate {
navigateBack()
}
return@launch
}
_state.update {
BookInfoState(book = book.first())
BookInfoState(book = book)
}
}
}

View file

@ -46,6 +46,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton
@ -55,7 +56,6 @@ import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.components.is_messages.IsError
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.browse.components.BrowseFileItem
import ua.acclorite.book_story.presentation.screens.browse.components.BrowseStoragePermissionDialog
@ -78,7 +78,7 @@ fun BrowseScreenRoot() {
BrowseScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
)
@ -92,7 +92,7 @@ fun BrowseScreenRoot() {
@Composable
private fun BrowseScreen(
state: State<BrowseState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (BrowseEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
@ -303,7 +303,11 @@ private fun BrowseScreen(
message = stringResource(id = R.string.browse_empty),
icon = painterResource(id = R.drawable.empty_browse),
actionTitle = stringResource(id = R.string.get_help),
action = { navigator.navigate(Screen.HELP, false) }
action = {
onNavigate {
navigate(Screen.Help(false))
}
}
)
}
@ -328,7 +332,9 @@ private fun BrowseScreen(
return@BackHandler
}
navigator.navigate(Screen.LIBRARY, false)
onNavigate {
navigate(Screen.Library)
}
}
}

View file

@ -27,7 +27,12 @@ import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/**
* Adding dialog. Adds all selected books to the Library.
* Adding dialog.
* Adds all selected books to the Library.
*
* @param state [BrowseState].
* @param onEvent [BrowseEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
*/
@Composable
fun BrowseAddingDialog(
@ -49,7 +54,7 @@ fun BrowseAddingDialog(
onAction = {
onEvent(
BrowseEvent.OnAddBooks(
navigator,
onNavigate = { navigator.it() },
resetScroll = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(0))
onLibraryEvent(LibraryEvent.OnLoadList)

View file

@ -8,8 +8,8 @@ import androidx.compose.ui.focus.FocusRequester
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.data.Navigator
import java.io.File
@Immutable
@ -21,7 +21,8 @@ sealed class BrowseEvent {
) : BrowseEvent()
data class OnStoragePermissionDismiss(
val permissionState: PermissionState, val showErrorMessage: () -> Unit
val permissionState: PermissionState,
val showErrorMessage: () -> Unit
) : BrowseEvent()
data object OnRefreshList : BrowseEvent()
@ -41,7 +42,7 @@ sealed class BrowseEvent {
data object OnAddingDialogDismiss : BrowseEvent()
data object OnGetBooksFromFiles : BrowseEvent()
data class OnAddBooks(
val navigator: Navigator,
val onNavigate: OnNavigate,
val resetScroll: () -> Unit,
val onFailed: () -> Unit,
val onSuccess: () -> Unit

View file

@ -396,10 +396,9 @@ class BrowseViewModel @Inject constructor(
}
event.resetScroll()
event.navigator.navigate(
Screen.LIBRARY,
false
)
event.onNavigate {
navigate(Screen.Library)
}
event.onSuccess()
_state.update {

View file

@ -33,13 +33,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
@ -63,7 +63,7 @@ import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
import ua.acclorite.book_story.presentation.screens.start.data.StartViewModel
@Composable
fun HelpScreenRoot() {
fun HelpScreenRoot(screen: Screen.Help) {
val navigator = LocalNavigator.current
val helpViewModel: HelpViewModel = hiltViewModel()
val browseViewModel: BrowseViewModel = hiltViewModel()
@ -73,12 +73,12 @@ fun HelpScreenRoot() {
val state = helpViewModel.state.collectAsState()
LaunchedEffect(Unit) {
helpViewModel.init(navigator)
helpViewModel.init(screen = screen)
}
HelpScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = helpViewModel::onEvent,
onMainEvent = mainViewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
@ -92,7 +92,7 @@ fun HelpScreenRoot() {
@Composable
private fun HelpScreen(
state: State<HelpState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit,
@ -113,7 +113,7 @@ private fun HelpScreen(
},
navigationIcon = {
if (!state.value.fromStart) {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
}
},
actions = {
@ -125,8 +125,10 @@ private fun HelpScreen(
) {
onStartEvent(StartEvent.OnResetStartScreen)
onMainEvent(MainEvent.OnChangeShowStartScreen(true))
navigator.navigateWithoutBackStack(Screen.START, false)
navigator.clearBackStack()
onNavigate {
navigate(Screen.Start, saveInBackStack = false)
clearBackStack()
}
}
}
},
@ -149,11 +151,11 @@ private fun HelpScreen(
.fillMaxWidth(),
shape = RoundedCornerShape(100),
onClick = {
navigator.clearArgument("from_start")
onBrowseEvent(BrowseEvent.OnLoadList)
onMainEvent(MainEvent.OnChangeShowStartScreen(false))
navigator.navigateWithoutBackStack(Screen.BROWSE, false)
onNavigate {
navigate(Screen.Browse, saveInBackStack = false)
}
}
) {
Text(text = stringResource(id = R.string.done))
@ -203,6 +205,7 @@ private fun HelpScreen(
item {
HelpAddBooksItem(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -210,6 +213,7 @@ private fun HelpScreen(
item {
HelpCustomizeApp(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -217,6 +221,7 @@ private fun HelpScreen(
item {
HelpMoveOrDeleteBooks(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -224,6 +229,7 @@ private fun HelpScreen(
item {
HelpEditBook(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -231,6 +237,7 @@ private fun HelpScreen(
item {
HelpReadBook(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -238,6 +245,7 @@ private fun HelpScreen(
item {
HelpCustomizeReader(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -245,6 +253,7 @@ private fun HelpScreen(
item {
HelpUpdateBook(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -252,6 +261,7 @@ private fun HelpScreen(
item {
HelpManageHistory(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}
@ -266,6 +276,7 @@ private fun HelpScreen(
item {
HelpSetUpTranslator(
state = state,
onNavigate = onNavigate,
onEvent = onEvent
)
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpAddBooksItem(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_add_books),
description = buildAnnotatedString {
@ -49,16 +48,18 @@ fun LazyItemScope.HelpAddBooksItem(
)
},
onTagClick = { tag ->
when (tag) {
"browse" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.BROWSE, true)
onNavigate {
when (tag) {
"browse" -> {
if (!state.value.fromStart) {
navigate(Screen.Browse, useBackAnimation = true)
}
}
}
"library" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.LIBRARY, true)
"library" -> {
if (!state.value.fromStart) {
navigate(Screen.Library, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpCustomizeApp(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_customize_app),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpCustomizeApp(
)
},
onTagClick = { tag ->
when (tag) {
"settings" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.SETTINGS, true)
onNavigate {
when (tag) {
"settings" -> {
if (!state.value.fromStart) {
navigate(Screen.Settings, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpCustomizeReader(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_customize_reader),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpCustomizeReader(
)
},
onTagClick = { tag ->
when (tag) {
"settings" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.SETTINGS, true)
onNavigate {
when (tag) {
"settings" -> {
if (!state.value.fromStart) {
navigate(Screen.Settings, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpEditBook(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_edit_book),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpEditBook(
)
},
onTagClick = { tag ->
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.LIBRARY, true)
onNavigate {
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigate(Screen.Library, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpManageHistory(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_manage_history),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpManageHistory(
)
},
onTagClick = { tag ->
when (tag) {
"history" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.HISTORY, true)
onNavigate {
when (tag) {
"history" -> {
if (!state.value.fromStart) {
navigate(Screen.History, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpMoveOrDeleteBooks(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_move_or_delete_books),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpMoveOrDeleteBooks(
)
},
onTagClick = { tag ->
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.LIBRARY, true)
onNavigate {
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigate(Screen.Library, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpReadBook(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_read_book),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpReadBook(
)
},
onTagClick = { tag ->
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.LIBRARY, true)
onNavigate {
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigate(Screen.Library, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpSetUpTranslator(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_set_up_translator),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpSetUpTranslator(
)
},
onTagClick = { tag ->
when (tag) {
"reader_settings" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.READER_SETTINGS, true)
onNavigate {
when (tag) {
"reader_settings" -> {
if (!state.value.fromStart) {
navigate(Screen.Settings.ReaderSettings, useBackAnimation = true)
}
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.help.components.HelpAnnotation
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
@ -16,10 +16,9 @@ import ua.acclorite.book_story.presentation.screens.help.data.HelpState
@Composable
fun LazyItemScope.HelpUpdateBook(
state: State<HelpState>,
onNavigate: OnNavigate,
onEvent: (HelpEvent) -> Unit
) {
val navigator = LocalNavigator.current
HelpItem(
title = stringResource(id = R.string.help_title_how_to_update_book),
description = buildAnnotatedString {
@ -44,10 +43,12 @@ fun LazyItemScope.HelpUpdateBook(
)
},
onTagClick = { tag ->
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigator.navigate(Screen.LIBRARY, true)
onNavigate {
when (tag) {
"library" -> {
if (!state.value.fromStart) {
navigate(Screen.Library, useBackAnimation = true)
}
}
}
}

View file

@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject
@HiltViewModel
@ -75,13 +75,11 @@ class HelpViewModel @Inject constructor(
}
}
fun init(navigator: Navigator) {
fun init(screen: Screen.Help) {
viewModelScope.launch {
val isFromStart = navigator.retrieveArgument("from_start") as? Boolean ?: false
_state.update {
it.copy(
fromStart = isFromStart
fromStart = screen.fromStart
)
}
}

View file

@ -38,6 +38,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
@ -47,9 +48,7 @@ import ua.acclorite.book_story.presentation.components.CustomSnackbar
import ua.acclorite.book_story.presentation.components.MoreDropDown
import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.history.components.HistoryDeleteWholeHistoryDialog
import ua.acclorite.book_story.presentation.screens.history.components.HistoryItem
@ -71,7 +70,7 @@ fun HistoryScreenRoot() {
HistoryScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
)
@ -84,7 +83,7 @@ fun HistoryScreenRoot() {
@Composable
private fun HistoryScreen(
state: State<HistoryState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (HistoryEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
@ -230,17 +229,17 @@ private fun HistoryScreen(
history = it,
isOnClickEnabled = !state.value.isRefreshing,
onBodyClick = {
navigator.navigate(
Screen.BOOK_INFO,
false,
Argument("book", it.bookId)
)
onNavigate {
navigate(
Screen.BookInfo(it.bookId)
)
}
},
onTitleClick = {
onEvent(
HistoryEvent.OnNavigateToReaderScreen(
navigator,
it.book!!
onNavigate = onNavigate,
book = it.book!!
)
)
},
@ -300,7 +299,9 @@ private fun HistoryScreen(
return@BackHandler
}
navigator.navigate(Screen.LIBRARY, false)
onNavigate {
navigate(Screen.Library)
}
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.focus.FocusRequester
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
@Immutable
sealed class HistoryEvent {
@ -27,7 +27,7 @@ sealed class HistoryEvent {
data class OnRequestFocus(val focusRequester: FocusRequester) : HistoryEvent()
data class OnUpdateBook(val book: Book) : HistoryEvent()
data class OnNavigateToReaderScreen(
val navigator: Navigator,
val onNavigate: OnNavigate,
val book: Book
) : HistoryEvent()

View file

@ -22,7 +22,6 @@ import ua.acclorite.book_story.domain.use_case.GetBooksById
import ua.acclorite.book_story.domain.use_case.GetHistory
import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.Screen
import java.text.SimpleDateFormat
import java.util.Calendar
@ -266,13 +265,9 @@ class HistoryViewModel @Inject constructor(
)
)
}
event.navigator.navigate(
Screen.READER,
false,
Argument(
"book", event.book.id
)
)
event.onNavigate {
navigate(Screen.Reader(event.book.id))
}
}
}

View file

@ -62,6 +62,7 @@ import kotlinx.coroutines.launch
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
@ -71,9 +72,7 @@ import ua.acclorite.book_story.presentation.components.MoreDropDown
import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.header
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
@ -100,7 +99,7 @@ fun LibraryScreenRoot() {
LibraryScreen(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
onHistoryEvent = historyViewModel::onEvent
@ -114,7 +113,7 @@ fun LibraryScreenRoot() {
@Composable
private fun LibraryScreen(
state: State<LibraryState>,
navigator: Navigator,
onNavigate: OnNavigate,
onEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit
@ -338,11 +337,9 @@ private fun LibraryScreen(
if (state.value.hasSelectedItems) {
onEvent(LibraryEvent.OnSelectBook(it))
} else {
navigator.navigate(
Screen.BOOK_INFO,
false,
Argument("book", it.first.id)
)
onNavigate {
navigate(Screen.BookInfo(it.first.id))
}
}
},
onLongClick = {
@ -353,8 +350,8 @@ private fun LibraryScreen(
onButtonClick = {
onEvent(
LibraryEvent.OnNavigateToReaderScreen(
navigator,
it.first
onNavigate = onNavigate,
book = it.first
)
)
}
@ -381,7 +378,9 @@ private fun LibraryScreen(
modifier = Modifier.align(Alignment.Center),
actionTitle = stringResource(id = R.string.add_book)
) {
navigator.navigate(Screen.BROWSE, false)
onNavigate {
navigate(Screen.Browse)
}
}
}
}

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.focus.FocusRequester
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.data.Navigator
@Immutable
sealed class LibraryEvent {
@ -30,7 +30,7 @@ sealed class LibraryEvent {
data class OnDeleteBooks(val refreshList: () -> Unit) : LibraryEvent()
data class OnUpdateBook(val book: Book) : LibraryEvent()
data class OnNavigateToReaderScreen(
val navigator: Navigator,
val onNavigate: OnNavigate,
val book: Book
) : LibraryEvent()
}

View file

@ -18,7 +18,6 @@ import ua.acclorite.book_story.domain.use_case.DeleteBooks
import ua.acclorite.book_story.domain.use_case.GetBooks
import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.Screen
import java.util.Date
import javax.inject.Inject
@ -338,13 +337,9 @@ class LibraryViewModel @Inject constructor(
)
)
}
event.navigator.navigate(
Screen.READER,
false,
Argument(
"book", event.book.id
)
)
event.onNavigate {
navigate(Screen.Reader(event.book.id))
}
}
}

View file

@ -50,6 +50,7 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomSelectionContainer
import ua.acclorite.book_story.presentation.components.customItems
@ -59,7 +60,7 @@ import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@ -77,7 +78,7 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
@Composable
fun ReaderScreenRoot() {
fun ReaderScreenRoot(screen: Screen.Reader) {
val navigator = LocalNavigator.current
val context = LocalContext.current as ComponentActivity
@ -103,8 +104,9 @@ fun ReaderScreenRoot() {
LaunchedEffect(Unit) {
viewModel.init(
navigator,
context,
screen = screen,
onNavigate = { navigator.it() },
context = context,
refreshList = {
libraryViewModel.onEvent(LibraryEvent.OnUpdateBook(it))
historyViewModel.onEvent(HistoryEvent.OnLoadList)
@ -139,8 +141,8 @@ fun ReaderScreenRoot() {
ReaderScreen(
state = state,
mainState = mainState,
navigator = navigator,
lazyListState = lazyListState,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onMainEvent = mainViewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent,
@ -153,8 +155,8 @@ fun ReaderScreenRoot() {
private fun ReaderScreen(
state: State<ReaderState>,
mainState: State<MainState>,
navigator: Navigator,
lazyListState: LazyListState,
onNavigate: OnNavigate,
onEvent: (ReaderEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
@ -278,6 +280,7 @@ private fun ReaderScreen(
) {
ReaderTopBar(
state = state,
onNavigate = onNavigate,
onEvent = onEvent,
onLibraryUpdateEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
@ -399,6 +402,7 @@ private fun ReaderScreen(
DisableSelection {
ReaderEndItem(
state = state,
onNavigate = onNavigate,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent,
@ -427,13 +431,14 @@ private fun ReaderScreen(
onEvent(
ReaderEvent.OnGoBack(
context,
navigator,
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
it.navigateBack()
onNavigate {
navigateBack()
}
}
)
)
@ -461,13 +466,14 @@ private fun ReaderScreen(
onEvent(
ReaderEvent.OnGoBack(
context,
navigator,
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
it.navigateBack()
onNavigate {
navigateBack()
}
}
)
)

View file

@ -30,7 +30,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@ -41,6 +41,7 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
* Reader end item. Displays at the end of the book.
*
* @param state [ReaderState].
* @param onNavigate Navigator callback.
* @param onEvent [ReaderEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
@ -49,13 +50,12 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
@Composable
fun ReaderEndItem(
state: State<ReaderState>,
onNavigate: OnNavigate,
onEvent: (ReaderEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit,
) {
val navigator = LocalNavigator.current
val context = LocalContext.current as ComponentActivity
val buttonText = remember {
context.getString(
if (state.value.book.category != Category.ALREADY_READ) R.string.move_to_read
@ -106,7 +106,7 @@ fun ReaderEndItem(
updatePage = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it))
},
navigator = navigator,
onNavigate = onNavigate,
context = context
)
)
@ -120,13 +120,14 @@ fun ReaderEndItem(
onEvent(
ReaderEvent.OnGoBack(
context,
navigator,
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
it.navigate(Screen.LIBRARY, true)
onNavigate {
navigate(Screen.Library, useBackAnimation = true)
}
}
)
)

View file

@ -27,8 +27,8 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.removeDigits
import ua.acclorite.book_story.presentation.data.removeTrailingZero
@ -42,6 +42,7 @@ import ua.acclorite.book_story.presentation.ui.Colors
* Reader top bar. Displays title of the book.
*
* @param state [ReaderState].
* @param onNavigate Navigator callback.
* @param onEvent [ReaderEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
@ -50,13 +51,12 @@ import ua.acclorite.book_story.presentation.ui.Colors
@Composable
fun ReaderTopBar(
state: State<ReaderState>,
onNavigate: OnNavigate,
onEvent: (ReaderEvent) -> Unit,
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
val navigator = LocalNavigator.current
val context = LocalContext.current as ComponentActivity
val progress by remember(state.value.book.progress) {
derivedStateOf {
(state.value.book.progress * 100)
@ -77,13 +77,14 @@ fun ReaderTopBar(
onEvent(
ReaderEvent.OnGoBack(
context,
navigator,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
it.navigateBack()
onNavigate {
navigateBack()
}
}
)
)
@ -108,16 +109,18 @@ fun ReaderTopBar(
onEvent(
ReaderEvent.OnGoBack(
context,
navigator,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
it.navigateWithoutBackStack(
Screen.BOOK_INFO,
true
)
onNavigate {
navigate(
Screen.BookInfo(state.value.book.id),
useBackAnimation = true,
saveInBackStack = false
)
}
}
)
)

View file

@ -8,8 +8,8 @@ import ua.acclorite.book_story.domain.model.LineWithTranslation
import ua.acclorite.book_story.domain.model.SelectableLanguage
import ua.acclorite.book_story.domain.util.ID
import ua.acclorite.book_story.domain.util.LanguageCode
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.data.Navigator
@Immutable
sealed class ReaderEvent {
@ -20,8 +20,10 @@ sealed class ReaderEvent {
val onTextIsEmpty: () -> Unit
) : ReaderEvent()
data class OnShowHideMenu(val show: Boolean? = null, val context: ComponentActivity) :
ReaderEvent()
data class OnShowHideMenu(
val show: Boolean? = null,
val context: ComponentActivity
) : ReaderEvent()
data class OnShowHideTranslatorBottomSheet(val show: Boolean? = null) : ReaderEvent()
@ -63,9 +65,8 @@ sealed class ReaderEvent {
data class OnGoBack(
val context: ComponentActivity,
val navigator: Navigator,
val refreshList: (Book) -> Unit,
val navigate: (Navigator) -> Unit
val navigate: () -> Unit,
val refreshList: (Book) -> Unit
) : ReaderEvent()
data class OnChangeProgress(
@ -82,7 +83,7 @@ sealed class ReaderEvent {
val context: ComponentActivity,
val onUpdateCategories: (Book) -> Unit,
val updatePage: (Int) -> Unit,
val navigator: Navigator
val onNavigate: OnNavigate
) : ReaderEvent()
data class OnOpenTranslator(

View file

@ -42,9 +42,9 @@ import ua.acclorite.book_story.domain.use_case.TranslateText
import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.domain.util.ID
import ua.acclorite.book_story.domain.util.LanguageCode
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.removeKey
import ua.acclorite.book_story.presentation.data.removeKeys
@ -912,7 +912,7 @@ class ReaderViewModel @Inject constructor(
}
insetsController.show(WindowInsetsCompat.Type.systemBars())
event.navigate(event.navigator)
event.navigate()
}
}
@ -967,7 +967,6 @@ class ReaderViewModel @Inject constructor(
onEvent(
ReaderEvent.OnGoBack(
event.context,
event.navigator,
refreshList = {},
navigate = {}
)
@ -995,7 +994,12 @@ class ReaderViewModel @Inject constructor(
}.size - 1
)
event.navigator.navigate(Screen.LIBRARY, true)
event.onNavigate {
navigate(
Screen.Library,
useBackAnimation = true
)
}
}
}
@ -1226,23 +1230,19 @@ class ReaderViewModel @Inject constructor(
}
fun init(
navigator: Navigator,
screen: Screen.Reader,
onNavigate: OnNavigate,
context: ComponentActivity,
refreshList: (Book) -> Unit,
onError: (UIText) -> Unit
) {
viewModelScope.launch(Dispatchers.IO) {
val bookId = navigator.retrieveArgument("book") as? Int
if (bookId == null) {
navigator.navigateBack()
return@launch
}
val book = getBookById.execute(bookId)
val book = getBookById.execute(screen.bookId)
if (book == null) {
navigator.navigateBack()
onNavigate {
navigateBack()
}
return@launch
}

View file

@ -24,10 +24,10 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.settings.components.SettingsCategoryItem
@ -35,13 +35,13 @@ import ua.acclorite.book_story.presentation.screens.settings.components.Settings
fun SettingsScreenRoot() {
val navigator = LocalNavigator.current
SettingsScreen(navigator = navigator)
SettingsScreen(onNavigate = { navigator.it() })
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SettingsScreen(
navigator: Navigator
onNavigate: OnNavigate
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
@ -57,7 +57,7 @@ private fun SettingsScreen(
Text(stringResource(id = R.string.settings_screen))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(
@ -83,7 +83,9 @@ private fun SettingsScreen(
text = stringResource(id = R.string.general_settings),
description = stringResource(id = R.string.general_settings_desc)
) {
navigator.navigate(Screen.GENERAL_SETTINGS, false)
onNavigate {
navigate(Screen.Settings.General)
}
}
}
@ -93,7 +95,9 @@ private fun SettingsScreen(
text = stringResource(id = R.string.appearance_settings),
description = stringResource(id = R.string.appearance_settings_desc)
) {
navigator.navigate(Screen.APPEARANCE_SETTINGS, false)
onNavigate {
navigate(Screen.Settings.Appearance)
}
}
}
@ -103,7 +107,9 @@ private fun SettingsScreen(
text = stringResource(id = R.string.reader_settings),
description = stringResource(id = R.string.reader_settings_desc)
) {
navigator.navigate(Screen.READER_SETTINGS, false)
onNavigate {
navigate(Screen.Settings.ReaderSettings)
}
}
}

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
@ -36,7 +37,6 @@ import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.theme_switcher.AppearanceSettingsThemeSwitcher
@ -58,7 +58,7 @@ fun AppearanceSettingsRoot() {
AppearanceSettings(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent
)
}
@ -67,7 +67,7 @@ fun AppearanceSettingsRoot() {
@Composable
private fun AppearanceSettings(
state: State<MainState>,
navigator: Navigator,
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
@ -91,7 +91,7 @@ private fun AppearanceSettings(
Text(stringResource(id = R.string.appearance_settings))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(

View file

@ -31,13 +31,13 @@ import com.google.accompanist.permissions.rememberPermissionState
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
@ -53,7 +53,7 @@ fun GeneralSettingsRoot() {
GeneralSettings(
state = state,
navigator = navigator,
onNavigate = { navigator.it() },
onSettingsEvent = settingsViewModel::onEvent,
onMainEvent = mainViewModel::onEvent
)
@ -64,7 +64,7 @@ fun GeneralSettingsRoot() {
@Composable
private fun GeneralSettings(
state: State<MainState>,
navigator: Navigator,
onNavigate: OnNavigate,
onSettingsEvent: (SettingsEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit
) {
@ -87,7 +87,7 @@ private fun GeneralSettings(
Text(stringResource(id = R.string.general_settings))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(

View file

@ -28,6 +28,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
@ -36,7 +37,6 @@ import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.screens.reader.components.translator_bottom_sheet.ReaderTranslatorBottomSheetLanguageChooser
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
@ -58,7 +58,7 @@ fun ReaderSettingsRoot() {
ReaderSettings(
state = state,
settingsState = settingsState,
navigator = navigator,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent,
onEvent = settingsViewModel::onEvent
)
@ -69,7 +69,7 @@ fun ReaderSettingsRoot() {
private fun ReaderSettings(
state: State<MainState>,
settingsState: State<SettingsState>,
navigator: Navigator,
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit,
onEvent: (SettingsEvent) -> Unit
) {
@ -145,7 +145,7 @@ private fun ReaderSettings(
Text(stringResource(id = R.string.reader_settings))
},
navigationIcon = {
GoBackButton(navigator = navigator)
GoBackButton(onNavigate = onNavigate)
},
scrollBehavior = scrollState.first,
colors = TopAppBarDefaults.largeTopAppBarColors(

View file

@ -35,7 +35,12 @@ import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
import ua.acclorite.book_story.presentation.screens.start.data.StartState
/**
* Done screen.
* Start Done screen.
*
* @param state [StartState].
* @param onEvent [StartEvent] callback.
* @param onMainEvent [MainEvent] callback.
* @param onBrowseEvent [BrowseEvent] callback.
*/
@Composable
fun StartDone(
@ -72,7 +77,7 @@ fun StartDone(
onClick = {
onEvent(
StartEvent.OnGoToBrowse(
navigator,
onNavigate = { navigator.it() },
onCompletedStartGuide = {
onBrowseEvent(BrowseEvent.OnLoadList)
onMainEvent(
@ -91,9 +96,7 @@ fun StartDone(
shape = RoundedCornerShape(100),
onClick = {
onEvent(
StartEvent.OnGoToHelp(
navigator
)
StartEvent.OnGoToHelp { navigator.it() }
)
}
) {

View file

@ -6,7 +6,7 @@ import androidx.activity.ComponentActivity
import androidx.compose.runtime.Immutable
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.domain.util.OnNavigate
@Immutable
sealed class StartEvent {
@ -23,10 +23,10 @@ sealed class StartEvent {
val onEnableUpdates: () -> Unit
) : StartEvent()
data class OnGoToBrowse(val navigator: Navigator, val onCompletedStartGuide: () -> Unit) :
data class OnGoToBrowse(val onNavigate: OnNavigate, val onCompletedStartGuide: () -> Unit) :
StartEvent()
data class OnGoToHelp(val navigator: Navigator) : StartEvent()
data class OnGoToHelp(val onNavigate: OnNavigate) : StartEvent()
data object OnResetStartScreen : StartEvent()
}

View file

@ -21,7 +21,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import ua.acclorite.book_story.domain.use_case.CheckForUpdates
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject
@ -239,13 +238,16 @@ class StartViewModel @Inject constructor(
}
is StartEvent.OnGoToBrowse -> {
event.navigator.navigateWithoutBackStack(Screen.BROWSE, false)
event.onNavigate {
navigate(Screen.Browse, saveInBackStack = false)
}
event.onCompletedStartGuide()
}
is StartEvent.OnGoToHelp -> {
event.navigator.putArgument(Argument("from_start", true))
event.navigator.navigateWithoutBackStack(Screen.HELP, false)
event.onNavigate {
navigate(Screen.Help(true), saveInBackStack = false)
}
}
is StartEvent.OnResetStartScreen -> {