🛠️ ViewModel passing code reformat

* Provided all viewModels through LocalComposition
* Removed passing viewModels as parameters
This commit is contained in:
Acclorite 2024-09-03 21:13:39 +03:00
parent 3b287ff1a0
commit b135ed301c
112 changed files with 802 additions and 1451 deletions

View file

@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.bottom_navigation_bar.BottomNavigationBar import ua.acclorite.book_story.presentation.components.bottom_navigation_bar.BottomNavigationBar
import ua.acclorite.book_story.presentation.components.custom_navigation_rail.CustomNavigationRail import ua.acclorite.book_story.presentation.components.custom_navigation_rail.CustomNavigationRail
import ua.acclorite.book_story.presentation.data.MainViewModel import ua.acclorite.book_story.presentation.data.MainViewModel
@ -89,8 +90,8 @@ class Activity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
setContent { setContent {
val state = LocalMainViewModel.current.state
val isLoaded = mainViewModel.isReady.collectAsState() val isLoaded = mainViewModel.isReady.collectAsState()
val state = mainViewModel.state.collectAsState()
val density = LocalDensity.current val density = LocalDensity.current
val imeInsets = WindowInsets.ime val imeInsets = WindowInsets.ime

View file

@ -0,0 +1,9 @@
package ua.acclorite.book_story.domain.util
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.StateFlow
abstract class BaseViewModel<S, E> : ViewModel() {
abstract val state: StateFlow<S>
abstract fun onEvent(event: E)
}

View file

@ -0,0 +1,91 @@
package ua.acclorite.book_story.presentation.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.domain.util.BaseViewModel
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.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutState
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
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.screens.about.nested.licenses.data.LicensesEvent
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesState
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesViewModel
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.help.data.HelpEvent
import ua.acclorite.book_story.presentation.screens.help.data.HelpState
import ua.acclorite.book_story.presentation.screens.help.data.HelpViewModel
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.history.data.HistoryState
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
import ua.acclorite.book_story.presentation.screens.start.data.StartState
import ua.acclorite.book_story.presentation.screens.start.data.StartViewModel
data class ViewModelComposition<S, E, V>(
val state: State<S>,
val onEvent: (E) -> Unit,
val viewModel: V
)
@Composable
inline fun <reified V : BaseViewModel<S, E>, S, E> viewModelComposition(
): ProvidableCompositionLocal<ViewModelComposition<S, E, V>> {
val viewModel: V = hiltViewModel()
val state = viewModel.state.collectAsState()
return compositionLocalOf {
ViewModelComposition(
state = state,
onEvent = viewModel::onEvent,
viewModel = viewModel
)
}
}
val LocalMainViewModel
@Composable get() = viewModelComposition<MainViewModel, MainState, MainEvent>()
val LocalLibraryViewModel
@Composable get() = viewModelComposition<LibraryViewModel, LibraryState, LibraryEvent>()
val LocalHistoryViewModel
@Composable get() = viewModelComposition<HistoryViewModel, HistoryState, HistoryEvent>()
val LocalBrowseViewModel
@Composable get() = viewModelComposition<BrowseViewModel, BrowseState, BrowseEvent>()
val LocalBookInfoViewModel
@Composable get() = viewModelComposition<BookInfoViewModel, BookInfoState, BookInfoEvent>()
val LocalReaderViewModel
@Composable get() = viewModelComposition<ReaderViewModel, ReaderState, ReaderEvent>()
val LocalSettingsViewModel
@Composable get() = viewModelComposition<SettingsViewModel, SettingsState, SettingsEvent>()
val LocalAboutViewModel
@Composable get() = viewModelComposition<AboutViewModel, AboutState, AboutEvent>()
val LocalLicensesViewModel
@Composable get() = viewModelComposition<LicensesViewModel, LicensesState, LicensesEvent>()
val LocalLicenseInfoViewModel
@Composable get() = viewModelComposition<LicenseInfoViewModel, LicenseInfoState, LicenseInfoEvent>()
val LocalHelpViewModel
@Composable get() = viewModelComposition<HelpViewModel, HelpState, HelpEvent>()
val LocalStartViewModel
@Composable get() = viewModelComposition<StartViewModel, StartState, StartEvent>()

View file

@ -82,6 +82,10 @@ data class MainState(
val browseIncludedFilterItems: List<String> = provideDefaultValue { emptyList() }, val browseIncludedFilterItems: List<String> = provideDefaultValue { emptyList() },
) : Parcelable { ) : Parcelable {
companion object { companion object {
private fun <D> provideDefaultValue(calculation: () -> D): D {
return calculation()
}
/** /**
* Initializes [MainState] by given [Map]. * Initializes [MainState] by given [Map].
* If no value provided in [data], assigns default value. * If no value provided in [data], assigns default value.
@ -209,8 +213,4 @@ data class MainState(
} }
} }
} }
}
private fun <D> provideDefaultValue(calculation: () -> D): D {
return calculation()
} }

View file

@ -1,7 +1,6 @@
package ua.acclorite.book_story.presentation.data package ua.acclorite.book_story.presentation.data
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -16,6 +15,7 @@ import ua.acclorite.book_story.domain.use_case.ChangeLanguage
import ua.acclorite.book_story.domain.use_case.CheckForUpdates import ua.acclorite.book_story.domain.use_case.CheckForUpdates
import ua.acclorite.book_story.domain.use_case.GetAllSettings import ua.acclorite.book_story.domain.use_case.GetAllSettings
import ua.acclorite.book_story.domain.use_case.SetDatastore import ua.acclorite.book_story.domain.use_case.SetDatastore
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.DataStoreConstants import ua.acclorite.book_story.domain.util.DataStoreConstants
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
@ -41,7 +41,7 @@ class MainViewModel @Inject constructor(
private val changeLanguage: ChangeLanguage, private val changeLanguage: ChangeLanguage,
private val checkForUpdates: CheckForUpdates, private val checkForUpdates: CheckForUpdates,
private val getAllSettings: GetAllSettings private val getAllSettings: GetAllSettings
) : ViewModel() { ) : BaseViewModel<MainState, MainEvent>() {
private val _isReady = MutableStateFlow(false) private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow() val isReady = _isReady.asStateFlow()
@ -52,9 +52,9 @@ class MainViewModel @Inject constructor(
private val _state: MutableStateFlow<MainState> = MutableStateFlow( private val _state: MutableStateFlow<MainState> = MutableStateFlow(
stateHandle[Constants.MAIN_STATE] ?: MainState() stateHandle[Constants.MAIN_STATE] ?: MainState()
) )
val state = _state.asStateFlow() override val state = _state.asStateFlow()
fun onEvent(event: MainEvent) { override fun onEvent(event: MainEvent) {
when (event) { when (event) {
is MainEvent.OnChangeLanguage -> { is MainEvent.OnChangeLanguage -> {
viewModelScope.launch(Dispatchers.Main) { viewModelScope.launch(Dispatchers.Main) {

View file

@ -44,6 +44,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Route import ua.acclorite.book_story.domain.util.Route
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.ui.Transitions import ua.acclorite.book_story.presentation.ui.Transitions
@ -54,11 +55,14 @@ private const val USE_BACK_ANIM = "use_back_animation"
private const val SCREENS = "screens" private const val SCREENS = "screens"
/** /**
* Passed in [CompositionLocalProvider] and can be accessed through [LocalNavigator].current. * Passed in [CompositionLocalProvider].
*/ */
val LocalNavigator = compositionLocalOf<Navigator> { val LocalNavigator = compositionLocalOf<Navigator> {
error("Cannot initialize Navigator.") error("Cannot initialize Navigator.")
} }
val LocalOnNavigate = compositionLocalOf<OnNavigate> {
error("Cannot initialize Navigator.")
}
/** /**
* Screens. Should be [Serializable] to be able to pass them inside [SavedStateHandle]. * Screens. Should be [Serializable] to be able to pass them inside [SavedStateHandle].
@ -434,7 +438,10 @@ fun NavigationHost(
.background(colorBetweenAnimations) .background(colorBetweenAnimations)
) )
CompositionLocalProvider(LocalNavigator provides navigator) { CompositionLocalProvider(
LocalNavigator provides navigator,
LocalOnNavigate provides { navigator.it() }
) {
content(navigator) content(navigator)
} }
} }

View file

@ -19,8 +19,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -29,51 +27,36 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.about.components.AboutItem import ua.acclorite.book_story.presentation.screens.about.components.AboutItem
import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog
import ua.acclorite.book_story.presentation.screens.about.components.badges.AboutBadges import ua.acclorite.book_story.presentation.screens.about.components.badges.AboutBadges
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutState
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
@Composable @Composable
fun AboutScreenRoot() { fun AboutScreenRoot() {
val navigator = LocalNavigator.current AboutScreen()
val aboutViewModel: AboutViewModel = hiltViewModel()
val state = aboutViewModel.state.collectAsState()
AboutScreen(
state = state,
onNavigate = { navigator.it() },
onEvent = aboutViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun AboutScreen( private fun AboutScreen() {
state: State<AboutState>, val state = LocalAboutViewModel.current.state
onNavigate: OnNavigate, val onEvent = LocalAboutViewModel.current.onEvent
onEvent: (AboutEvent) -> Unit val onNavigate = LocalOnNavigate.current
) {
val context = LocalContext.current val context = LocalContext.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
if (state.value.showUpdateDialog) { if (state.value.showUpdateDialog) {
AboutUpdateDialog( AboutUpdateDialog()
state = state,
onEvent = onEvent
)
} }
Scaffold( Scaffold(
@ -245,7 +228,7 @@ private fun AboutScreen(
} }
item { item {
AboutBadges(onEvent = onEvent) AboutBadges()
} }
} }
} }

View file

@ -3,24 +3,22 @@ package ua.acclorite.book_story.presentation.screens.about.components
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Update import androidx.compose.material.icons.filled.Update
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutState
/** /**
* Update dialog. If there is app update transfers to the download page. * Update dialog. If there is app update transfers to the download page.
*/ */
@Composable @Composable
fun AboutUpdateDialog( fun AboutUpdateDialog() {
state: State<AboutState>, val state = LocalAboutViewModel.current.state
onEvent: (AboutEvent) -> Unit val onEvent = LocalAboutViewModel.current.onEvent
) {
val context = LocalContext.current val context = LocalContext.current
val update = remember(state.value.showUpdateDialog) { val update = remember(state.value.showUpdateDialog) {

View file

@ -13,6 +13,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
@ -21,14 +22,11 @@ import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
* About Badges. * About Badges.
* Links all ways to contact me. * Links all ways to contact me.
* *
* @param onEvent [AboutEvent] callback.
* @param verticalPadding Vertical item padding. * @param verticalPadding Vertical item padding.
*/ */
@Composable @Composable
fun AboutBadges( fun AboutBadges(verticalPadding: Dp = 18.dp) {
onEvent: (AboutEvent) -> Unit, val onEvent = LocalAboutViewModel.current.onEvent
verticalPadding: Dp = 18.dp
) {
val context = LocalContext.current val context = LocalContext.current
Box( Box(

View file

@ -3,7 +3,6 @@ package ua.acclorite.book_story.presentation.screens.about.data
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -12,18 +11,19 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.use_case.CheckForUpdates import ua.acclorite.book_story.domain.use_case.CheckForUpdates
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.presentation.data.launchActivity import ua.acclorite.book_story.presentation.data.launchActivity
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class AboutViewModel @Inject constructor( class AboutViewModel @Inject constructor(
private val checkForUpdates: CheckForUpdates, private val checkForUpdates: CheckForUpdates,
) : ViewModel() { ) : BaseViewModel<AboutState, AboutEvent>() {
private val _state = MutableStateFlow(AboutState()) private val _state = MutableStateFlow(AboutState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
fun onEvent(event: AboutEvent) { override fun onEvent(event: AboutEvent) {
when (event) { when (event) {
is AboutEvent.OnNavigateToBrowserPage -> { is AboutEvent.OnNavigateToBrowserPage -> {
viewModelScope.launch { viewModelScope.launch {

View file

@ -16,40 +16,32 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent 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 import ua.acclorite.book_story.presentation.screens.about.nested.credits.components.CreditItem
@Composable @Composable
fun CreditsScreenRoot() { fun CreditsScreenRoot() {
val navigator = LocalNavigator.current CreditsScreen()
val aboutViewModel: AboutViewModel = hiltViewModel()
CreditsScreen(
onNavigate = { navigator.it() },
onAboutNavigateEvent = aboutViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun CreditsScreen( private fun CreditsScreen() {
onNavigate: OnNavigate, val onEvent = LocalAboutViewModel.current.onEvent
onAboutNavigateEvent: (AboutEvent.OnNavigateToBrowserPage) -> Unit val onNavigate = LocalOnNavigate.current
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
val context = LocalContext.current val context = LocalContext.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
@ -81,7 +73,7 @@ private fun CreditsScreen(
customItems(Constants.CREDITS, key = { it.name }) { customItems(Constants.CREDITS, key = { it.name }) {
CreditItem(credit = it) { CreditItem(credit = it) {
it.website?.let { website -> it.website?.let { website ->
onAboutNavigateEvent( onEvent(
AboutEvent.OnNavigateToBrowserPage( AboutEvent.OnNavigateToBrowserPage(
page = website, page = website,
context = context, context = context,

View file

@ -23,8 +23,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -33,56 +31,46 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R 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.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalLicenseInfoViewModel
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
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.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.DefaultTransition import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.SlidingTransition import ua.acclorite.book_story.presentation.ui.SlidingTransition
@Composable @Composable
fun LicenseInfoScreenRoot(screen: Screen.About.LicenseInfo) { fun LicenseInfoScreenRoot(screen: Screen.About.LicenseInfo) {
val navigator = LocalNavigator.current val viewModel = LocalLicenseInfoViewModel.current.viewModel
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current val context = LocalContext.current
val licenseInfoViewModel: LicenseInfoViewModel = hiltViewModel()
val state = licenseInfoViewModel.state.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
licenseInfoViewModel.init( viewModel.init(
screen = screen, screen = screen,
onNavigate = { navigator.it() }, onNavigate = onNavigate,
context = context context = context
) )
} }
LicenseInfoScreen( LicenseInfoScreen()
state = state,
onNavigate = { navigator.it() },
onEvent = licenseInfoViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun LicenseInfoScreen( private fun LicenseInfoScreen() {
state: State<LicenseInfoState>, val state = LocalLicenseInfoViewModel.current.state
onNavigate: OnNavigate, val onEvent = LocalLicenseInfoViewModel.current.onEvent
onEvent: (LicenseInfoEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
val context = LocalContext.current val context = LocalContext.current
val onNavigate = LocalOnNavigate.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
val licenses = remember(state.value.license?.licenses) { val licenses = remember(state.value.license?.licenses) {
state.value.license?.licenses?.toList() state.value.license?.licenses?.toList()
} }

View file

@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withContext import com.mikepenz.aboutlibraries.util.withContext
@ -14,18 +13,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.launchActivity import ua.acclorite.book_story.presentation.data.launchActivity
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class LicenseInfoViewModel @Inject constructor() : ViewModel() { class LicenseInfoViewModel @Inject constructor(
) : BaseViewModel<LicenseInfoState, LicenseInfoEvent>() {
private val _state = MutableStateFlow(LicenseInfoState()) private val _state = MutableStateFlow(LicenseInfoState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
fun onEvent(event: LicenseInfoEvent) { override fun onEvent(event: LicenseInfoEvent) {
when (event) { when (event) {
is LicenseInfoEvent.OnOpenLicensePage -> { is LicenseInfoEvent.OnOpenLicensePage -> {
viewModelScope.launch { viewModelScope.launch {

View file

@ -18,51 +18,40 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R 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.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalLicensesViewModel
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen 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.components.LicenseItem
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesState
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesViewModel
@Composable @Composable
fun LicensesScreenRoot() { fun LicensesScreenRoot() {
val navigator = LocalNavigator.current val viewModel = LocalLicensesViewModel.current.viewModel
val context = LocalContext.current val context = LocalContext.current
val licensesViewModel: LicensesViewModel = hiltViewModel()
val state = licensesViewModel.state.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
licensesViewModel.init(context) viewModel.init(context)
} }
LicensesScreen( LicensesScreen()
state = state,
onNavigate = { navigator.it() }
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun LicensesScreen( private fun LicensesScreen() {
state: State<LicensesState>, val state = LocalLicensesViewModel.current.state
onNavigate: OnNavigate val onNavigate = LocalOnNavigate.current
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState( val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState(
state.value.listState state.value.listState
) )

View file

@ -0,0 +1,6 @@
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
import androidx.compose.runtime.Immutable
@Immutable
sealed class LicensesEvent

View file

@ -1,7 +1,6 @@
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
import android.content.Context import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withJson import com.mikepenz.aboutlibraries.util.withJson
@ -12,15 +11,18 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.BaseViewModel
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class LicensesViewModel @Inject constructor( class LicensesViewModel @Inject constructor(
) : ViewModel() { ) : BaseViewModel<LicensesState, LicensesEvent>() {
private val _state = MutableStateFlow(LicensesState()) private val _state = MutableStateFlow(LicensesState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
override fun onEvent(event: LicensesEvent) {}
fun init(context: Context) { fun init(context: Context) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {

View file

@ -37,20 +37,17 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R 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.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomSnackbar import ua.acclorite.book_story.presentation.components.CustomSnackbar
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen 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.BookInfoBackground
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoDescriptionSection import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoDescriptionSection
@ -63,60 +60,36 @@ import ua.acclorite.book_story.presentation.screens.book_info.components.details
import ua.acclorite.book_story.presentation.screens.book_info.components.dialog.BookInfoDeleteDialog import ua.acclorite.book_story.presentation.screens.book_info.components.dialog.BookInfoDeleteDialog
import ua.acclorite.book_story.presentation.screens.book_info.components.dialog.BookInfoMoveDialog import ua.acclorite.book_story.presentation.screens.book_info.components.dialog.BookInfoMoveDialog
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
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
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
@Composable @Composable
fun BookInfoScreenRoot(screen: Screen.BookInfo) { fun BookInfoScreenRoot(screen: Screen.BookInfo) {
val navigator = LocalNavigator.current val viewModel = LocalBookInfoViewModel.current.viewModel
val onNavigate = LocalOnNavigate.current
val viewModel: BookInfoViewModel = hiltViewModel()
val libraryViewModel: LibraryViewModel = hiltViewModel()
val historyViewModel: HistoryViewModel = hiltViewModel()
val browseViewModel: BrowseViewModel = hiltViewModel()
val state = viewModel.state.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.init( viewModel.init(
screen = screen, screen = screen,
onNavigate = { navigator.it() } onNavigate = onNavigate
) )
} }
BookInfoScreen()
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { onDispose {
viewModel.clearViewModel() viewModel.clearViewModel()
} }
} }
BookInfoScreen(
state = state,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
onHistoryEvent = historyViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
private fun BookInfoScreen( private fun BookInfoScreen() {
state: State<BookInfoState>,
onNavigate: OnNavigate,
onEvent: (BookInfoEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val listState = rememberLazyListState() val listState = rememberLazyListState()
val snackbarState = remember { SnackbarHostState() } val snackbarState = remember { SnackbarHostState() }
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
@ -132,42 +105,19 @@ private fun BookInfoScreen(
) )
if (state.value.showChangeCoverBottomSheet) { if (state.value.showChangeCoverBottomSheet) {
BookInfoChangeCoverBottomSheet( BookInfoChangeCoverBottomSheet()
state = state,
onEvent = onEvent,
onLibraryUpdateEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
)
} }
if (state.value.showDetailsBottomSheet) { if (state.value.showDetailsBottomSheet) {
BookInfoDetailsBottomSheet( BookInfoDetailsBottomSheet()
state = state,
onEvent = onEvent
)
} }
if (state.value.showDeleteDialog) { if (state.value.showDeleteDialog) {
BookInfoDeleteDialog( BookInfoDeleteDialog()
onEvent = onEvent,
onLibraryLoadEvent = onLibraryEvent,
onHistoryLoadEvent = onHistoryEvent,
onBrowseLoadEvent = onBrowseEvent
)
} }
if (state.value.showMoveDialog) { if (state.value.showMoveDialog) {
BookInfoMoveDialog( BookInfoMoveDialog()
state = state,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
)
} }
if (state.value.showConfirmUpdateDialog) { if (state.value.showConfirmUpdateDialog) {
BookInfoConfirmUpdateDialog( BookInfoConfirmUpdateDialog(snackbarHostState = snackbarState)
snackbarHostState = snackbarState,
onEvent = onEvent,
onLibraryUpdateEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
)
} }
Scaffold( Scaffold(
@ -179,11 +129,6 @@ private fun BookInfoScreen(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
topBar = { topBar = {
BookInfoTopBar( BookInfoTopBar(
state = state,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent,
onHistoryEvent = onHistoryEvent,
onNavigate = onNavigate,
listState = listState, listState = listState,
snackbarState = snackbarState snackbarState = snackbarState
) )
@ -216,7 +161,7 @@ private fun BookInfoScreen(
Column(Modifier.fillMaxWidth()) { Column(Modifier.fillMaxWidth()) {
Spacer(modifier = Modifier.height(paddingValues.calculateTopPadding() + 12.dp)) Spacer(modifier = Modifier.height(paddingValues.calculateTopPadding() + 12.dp))
// Info // Info
BookInfoInfoSection(state = state, onEvent = onEvent) BookInfoInfoSection()
} }
} }
} }
@ -224,16 +169,13 @@ private fun BookInfoScreen(
item { item {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
// Statistic // Statistic
BookInfoStatisticSection(state = state) BookInfoStatisticSection()
} }
item { item {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
// Description // Description
BookInfoDescriptionSection( BookInfoDescriptionSection()
state = state,
onEvent = onEvent
)
} }
} }

View file

@ -13,7 +13,6 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -26,21 +25,18 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
/** /**
* Description section. * Description section.
*
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
*/ */
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BookInfoDescriptionSection( fun BookInfoDescriptionSection() {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit val onEvent = LocalBookInfoViewModel.current.onEvent
) {
val descriptionFocusRequester = remember { FocusRequester() } val descriptionFocusRequester = remember { FocusRequester() }
if (!state.value.editDescription) { if (!state.value.editDescription) {

View file

@ -25,7 +25,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -40,21 +39,18 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomCoverImage import ua.acclorite.book_story.presentation.components.CustomCoverImage
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
/** /**
* BookInfo's Info section. * BookInfo's Info section.
*
* @param state [BookInfoState] instance.
* @param onEvent [BookInfoEvent] callback.
*/ */
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BookInfoInfoSection( fun BookInfoInfoSection() {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit val onEvent = LocalBookInfoViewModel.current.onEvent
) {
val titleFocusRequester = remember { FocusRequester() } val titleFocusRequester = remember { FocusRequester() }
val authorFocusRequester = remember { FocusRequester() } val authorFocusRequester = remember { FocusRequester() }

View file

@ -9,7 +9,6 @@ import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -20,18 +19,17 @@ import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomDropDownMenuItem import ua.acclorite.book_story.presentation.components.CustomDropDownMenuItem
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
/** /**
* Book info more dropdown. Has three options: Delete, Move and Details. * Book info more dropdown. Has three options: Delete, Move and Details.
*/ */
@Composable @Composable
fun BookInfoMoreDropDown( fun BookInfoMoreDropDown(snackbarState: SnackbarHostState) {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit, val onEvent = LocalBookInfoViewModel.current.onEvent
snackbarState: SnackbarHostState
) {
var showDropDown by remember { mutableStateOf(false) } var showDropDown by remember { mutableStateOf(false) }
Box { Box {

View file

@ -9,7 +9,6 @@ import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
@ -17,15 +16,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.data.removeDigits import ua.acclorite.book_story.presentation.data.removeDigits
import ua.acclorite.book_story.presentation.data.removeTrailingZero import ua.acclorite.book_story.presentation.data.removeTrailingZero
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
/** /**
* Statistic section. * Statistic section.
*/ */
@Composable @Composable
fun BookInfoStatisticSection(state: State<BookInfoState>) { fun BookInfoStatisticSection() {
val state = LocalBookInfoViewModel.current.state
val progress = remember(state.value.book) { val progress = remember(state.value.book) {
"${ "${
(state.value.book.progress * 100) (state.value.book.progress * 100)

View file

@ -13,20 +13,21 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import ua.acclorite.book_story.R 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.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.ui.DefaultTransition import ua.acclorite.book_story.presentation.ui.DefaultTransition
@ -35,26 +36,22 @@ import ua.acclorite.book_story.presentation.ui.Transitions
/** /**
* Book Info Top Bar. * Book Info Top Bar.
* *
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryEvent [HistoryEvent] callback.
* @param onNavigate Navigator callback.
* @param listState [LazyListState]. * @param listState [LazyListState].
* @param snackbarState [SnackbarHostState]. * @param snackbarState [SnackbarHostState].
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun BookInfoTopBar( fun BookInfoTopBar(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
onNavigate: OnNavigate,
listState: LazyListState, listState: LazyListState,
snackbarState: SnackbarHostState snackbarState: SnackbarHostState
) { ) {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current val context = LocalContext.current
val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } } val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
AnimatedTopAppBar( AnimatedTopAppBar(
@ -125,11 +122,7 @@ fun BookInfoTopBar(
!state.value.editAuthor && !state.value.editAuthor &&
!state.value.editDescription !state.value.editDescription
) { ) {
BookInfoMoreDropDown( BookInfoMoreDropDown(snackbarState = snackbarState)
state = state,
onEvent = onEvent,
snackbarState = snackbarState
)
} }
CustomAnimatedVisibility( CustomAnimatedVisibility(
visible = state.value.editTitle || visible = state.value.editTitle ||
@ -175,7 +168,8 @@ fun BookInfoTopBar(
onLibraryEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
} }
)) )
)
} }
} }
} }

View file

@ -13,35 +13,31 @@ import androidx.compose.material.icons.filled.ImageSearch
import androidx.compose.material.icons.filled.Restore import androidx.compose.material.icons.filled.Restore
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomBottomSheet import ua.acclorite.book_story.presentation.components.CustomBottomSheet
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/** /**
* Change cover bottom sheet. Lets user select photo from the gallery and replaces old one. * Change cover bottom sheet. Lets user select photo from the gallery and replaces old one.
*
* @param state [BookInfoState] instance.
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/ */
@Composable @Composable
fun BookInfoChangeCoverBottomSheet( fun BookInfoChangeCoverBottomSheet() {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit, val onHistoryEvent = LocalHistoryViewModel.current.onEvent
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit val onEvent = LocalBookInfoViewModel.current.onEvent
) {
val context = LocalContext.current val context = LocalContext.current
val photoPicker = rememberLauncherForActivityResult( val photoPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(), contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri -> onResult = { uri ->
@ -51,8 +47,8 @@ fun BookInfoChangeCoverBottomSheet(
uri, uri,
context, context,
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
} }
) )
) )
@ -83,8 +79,8 @@ fun BookInfoChangeCoverBottomSheet(
onEvent( onEvent(
BookInfoEvent.OnResetCoverImage( BookInfoEvent.OnResetCoverImage(
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
showResult = { showResult = {
it.asString(context).showToast(context = context) it.asString(context).showToast(context = context)
@ -117,8 +113,8 @@ fun BookInfoChangeCoverBottomSheet(
onEvent( onEvent(
BookInfoEvent.OnDeleteCover( BookInfoEvent.OnDeleteCover(
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
} }
) )
) )

View file

@ -7,6 +7,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
@ -17,17 +20,12 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
* Updates book if action clicked. * Updates book if action clicked.
* *
* @param snackbarHostState [SnackbarHostState]. * @param snackbarHostState [SnackbarHostState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/ */
@Composable @Composable
fun BookInfoConfirmUpdateDialog( fun BookInfoConfirmUpdateDialog(snackbarHostState: SnackbarHostState) {
snackbarHostState: SnackbarHostState, val onEvent = LocalBookInfoViewModel.current.onEvent
onEvent: (BookInfoEvent) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit, val onHistoryEvent = LocalHistoryViewModel.current.onEvent
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit,
) {
val context = LocalContext.current val context = LocalContext.current
CustomDialogWithContent( CustomDialogWithContent(
@ -45,20 +43,12 @@ fun BookInfoConfirmUpdateDialog(
snackbarState = snackbarHostState, snackbarState = snackbarHostState,
context = context, context = context,
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
} }
) )
) )
}, },
withDivider = false withDivider = false
) )
} }

View file

@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -12,9 +11,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomBottomSheet import ua.acclorite.book_story.presentation.components.CustomBottomSheet
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@ -22,15 +21,11 @@ import java.util.Locale
/** /**
* Details bottom sheet. Displays name, path, last opened and size of the file. * Details bottom sheet. Displays name, path, last opened and size of the file.
*
* @param state [BookInfoState] instance.
* @param onEvent [BookInfoEvent] callback.
*/ */
@Composable @Composable
fun BookInfoDetailsBottomSheet( fun BookInfoDetailsBottomSheet() {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit val onEvent = LocalBookInfoViewModel.current.onEvent
) {
val context = LocalContext.current val context = LocalContext.current
val pattern = remember { val pattern = remember {

View file

@ -6,8 +6,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
@ -16,20 +20,14 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/** /**
* Delete dialog. Deletes current book. * Delete dialog. Deletes current book.
*
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryLoadEvent [LibraryEvent] callback.
* @param onHistoryLoadEvent [HistoryEvent] callback.
* @param onBrowseLoadEvent [BrowseEvent] callback.
*/ */
@Composable @Composable
fun BookInfoDeleteDialog( fun BookInfoDeleteDialog() {
onEvent: (BookInfoEvent) -> Unit, val onEvent = LocalBookInfoViewModel.current.onEvent
onLibraryLoadEvent: (LibraryEvent.OnLoadList) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit, val onHistoryEvent = LocalHistoryViewModel.current.onEvent
onBrowseLoadEvent: (BrowseEvent.OnLoadList) -> Unit val onBrowseEvent = LocalBrowseViewModel.current.onEvent
) { val onNavigate = LocalOnNavigate.current
val navigator = LocalNavigator.current
val context = LocalContext.current val context = LocalContext.current
CustomDialogWithContent( CustomDialogWithContent(
@ -45,11 +43,11 @@ fun BookInfoDeleteDialog(
onAction = { onAction = {
onEvent( onEvent(
BookInfoEvent.OnDeleteBook( BookInfoEvent.OnDeleteBook(
onNavigate = { navigator.it() }, onNavigate = onNavigate,
refreshList = { refreshList = {
onLibraryLoadEvent(LibraryEvent.OnLoadList) onLibraryEvent(LibraryEvent.OnLoadList)
onBrowseLoadEvent(BrowseEvent.OnLoadList) onBrowseEvent(BrowseEvent.OnLoadList)
onHistoryLoadEvent(HistoryEvent.OnLoadList) onHistoryEvent(HistoryEvent.OnLoadList)
} }
) )
) )

View file

@ -4,39 +4,34 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.DriveFileMove import androidx.compose.material.icons.automirrored.outlined.DriveFileMove
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Category import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.presentation.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.components.custom_dialog.SelectableDialogItem import ua.acclorite.book_story.presentation.components.custom_dialog.SelectableDialogItem
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/** /**
* Move dialog. * Move dialog.
* Moves current book to the selected category. * Moves current book to the selected category.
*
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/ */
@Composable @Composable
fun BookInfoMoveDialog( fun BookInfoMoveDialog() {
state: State<BookInfoState>, val state = LocalBookInfoViewModel.current.state
onEvent: (BookInfoEvent) -> Unit, val onEvent = LocalBookInfoViewModel.current.onEvent
onLibraryEvent: (LibraryEvent) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit val onHistoryEvent = LocalHistoryViewModel.current.onEvent
) { val onNavigate = LocalOnNavigate.current
val navigator = LocalNavigator.current
val context = LocalContext.current val context = LocalContext.current
val categories = remember { val categories = remember {
@ -61,12 +56,12 @@ fun BookInfoMoveDialog(
BookInfoEvent.OnMoveBook( BookInfoEvent.OnMoveBook(
refreshList = { refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
updatePage = { updatePage = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it)) onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it))
}, },
onNavigate = { navigator.it() } onNavigate = onNavigate
) )
) )
context.getString(R.string.book_moved) context.getString(R.string.book_moved)

View file

@ -8,7 +8,6 @@ import android.content.Context.CLIPBOARD_SERVICE
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Build import android.os.Build
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -34,6 +33,7 @@ import ua.acclorite.book_story.domain.use_case.ResetCoverImage
import ua.acclorite.book_story.domain.use_case.UpdateBookWithText 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.UpdateBooks
import ua.acclorite.book_story.domain.use_case.UpdateCoverImageOfBook import ua.acclorite.book_story.domain.use_case.UpdateCoverImageOfBook
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.UIText import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
@ -54,17 +54,17 @@ class BookInfoViewModel @Inject constructor(
private val getText: GetText, private val getText: GetText,
private val canResetCover: CanResetCover, private val canResetCover: CanResetCover,
private val resetCoverImage: ResetCoverImage, private val resetCoverImage: ResetCoverImage,
) : ViewModel() { ) : BaseViewModel<BookInfoState, BookInfoEvent>() {
private val _state = MutableStateFlow(BookInfoState()) private val _state = MutableStateFlow(BookInfoState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private var eventJob = SupervisorJob() private var eventJob = SupervisorJob()
private var snackBarJob: Job? = null private var snackBarJob: Job? = null
private var updateJob: Job? = null private var updateJob: Job? = null
fun onEvent(event: BookInfoEvent) { override fun onEvent(event: BookInfoEvent) {
viewModelScope.launch(eventJob + Dispatchers.Main) { viewModelScope.launch(eventJob + Dispatchers.Main) {
when (event) { when (event) {
is BookInfoEvent.OnShowHideChangeCoverBottomSheet -> { is BookInfoEvent.OnShowHideChangeCoverBottomSheet -> {

View file

@ -14,24 +14,19 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.SelectableFile import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.browse.components.BrowseEmptyPlaceholder import ua.acclorite.book_story.presentation.screens.browse.components.BrowseEmptyPlaceholder
@ -41,23 +36,16 @@ import ua.acclorite.book_story.presentation.screens.browse.components.filter_bot
import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseLayout import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseLayout
import ua.acclorite.book_story.presentation.screens.browse.components.top_bar.BrowseTopBar import ua.acclorite.book_story.presentation.screens.browse.components.top_bar.BrowseTopBar
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
import ua.acclorite.book_story.presentation.ui.DefaultTransition import ua.acclorite.book_story.presentation.ui.DefaultTransition
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@Composable @Composable
fun BrowseScreenRoot() { fun BrowseScreenRoot() {
val navigator = LocalNavigator.current val state = LocalBrowseViewModel.current.state
val viewModel: BrowseViewModel = hiltViewModel() val mainState = LocalMainViewModel.current.state
val libraryViewModel: LibraryViewModel = hiltViewModel() val onEvent = LocalBrowseViewModel.current.onEvent
val mainViewModel: MainViewModel = hiltViewModel() val viewModel = LocalBrowseViewModel.current.viewModel
val state = viewModel.state.collectAsState()
val mainState = mainViewModel.state.collectAsState()
val permissionState = rememberPermissionState( val permissionState = rememberPermissionState(
permission = Manifest.permission.READ_EXTERNAL_STORAGE permission = Manifest.permission.READ_EXTERNAL_STORAGE
@ -72,22 +60,14 @@ fun BrowseScreenRoot() {
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.onEvent(BrowseEvent.OnUpdateScrollOffset) onEvent(BrowseEvent.OnUpdateScrollOffset)
viewModel.onEvent( onEvent(BrowseEvent.OnPermissionCheck(permissionState))
BrowseEvent.OnPermissionCheck(permissionState)
)
} }
BrowseScreen( BrowseScreen(
state = state,
mainState = mainState,
permissionState = permissionState, permissionState = permissionState,
filteredFiles = filteredFiles.value, filteredFiles = filteredFiles.value,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onMainEvent = mainViewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
) )
DisposableEffect(Unit) { DisposableEffect(Unit) {
@ -103,16 +83,15 @@ fun BrowseScreenRoot() {
) )
@Composable @Composable
private fun BrowseScreen( private fun BrowseScreen(
state: State<BrowseState>,
mainState: State<MainState>,
permissionState: PermissionState, permissionState: PermissionState,
filteredFiles: List<SelectableFile>, filteredFiles: List<SelectableFile>
onNavigate: OnNavigate,
onEvent: (BrowseEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) { ) {
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current val context = LocalContext.current
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
refreshing = state.value.isRefreshing, refreshing = state.value.isRefreshing,
onRefresh = { onRefresh = {
@ -121,22 +100,13 @@ private fun BrowseScreen(
) )
if (state.value.requestPermissionDialog) { if (state.value.requestPermissionDialog) {
BrowseStoragePermissionDialog(onEvent, permissionState) BrowseStoragePermissionDialog(permissionState)
} }
if (state.value.showAddingDialog) { if (state.value.showAddingDialog) {
BrowseAddingDialog( BrowseAddingDialog()
state = state,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent
)
} }
if (state.value.showFilterBottomSheet) { if (state.value.showFilterBottomSheet) {
BrowseFilterBottomSheet( BrowseFilterBottomSheet()
state = state,
mainState = mainState,
onMainEvent = onMainEvent,
onEvent = onEvent
)
} }
Scaffold( Scaffold(
@ -145,12 +115,7 @@ private fun BrowseScreen(
.pullRefresh(refreshState), .pullRefresh(refreshState),
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
topBar = { topBar = {
BrowseTopBar( BrowseTopBar(filteredFiles = filteredFiles)
state = state,
mainState = mainState,
filteredFiles = filteredFiles,
onEvent = onEvent
)
} }
) { padding -> ) { padding ->
Box( Box(
@ -160,8 +125,6 @@ private fun BrowseScreen(
) { ) {
DefaultTransition(visible = !state.value.isLoading) { DefaultTransition(visible = !state.value.isLoading) {
BrowseLayout( BrowseLayout(
state = state,
mainState = mainState,
filteredFiles = filteredFiles, filteredFiles = filteredFiles,
onLongItemClick = { selectableFile -> onLongItemClick = { selectableFile ->
when (selectableFile.isDirectory) { when (selectableFile.isDirectory) {
@ -223,11 +186,8 @@ private fun BrowseScreen(
} }
BrowseEmptyPlaceholder( BrowseEmptyPlaceholder(
state = state,
isFilesEmpty = filteredFiles.isEmpty(), isFilesEmpty = filteredFiles.isEmpty(),
storagePermissionState = permissionState, storagePermissionState = permissionState,
onNavigate = onNavigate,
onEvent = onEvent
) )
PullRefreshIndicator( PullRefreshIndicator(
@ -255,9 +215,7 @@ private fun BrowseScreen(
state.value.inNestedDirectory state.value.inNestedDirectory
&& mainState.value.browseFilesStructure != BrowseFilesStructure.ALL_FILES && mainState.value.browseFilesStructure != BrowseFilesStructure.ALL_FILES
) { ) {
onEvent( onEvent(BrowseEvent.OnGoBackDirectory)
BrowseEvent.OnGoBackDirectory
)
return@BackHandler return@BackHandler
} }

View file

@ -2,7 +2,6 @@ package ua.acclorite.book_story.presentation.screens.browse.components
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -10,34 +9,32 @@ import androidx.compose.ui.res.stringResource
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.PermissionState
import ua.acclorite.book_story.R 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.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty 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.components.is_messages.IsError
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen 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.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.ui.Transitions import ua.acclorite.book_story.presentation.ui.Transitions
/** /**
* Browse Empty Placeholder. * Browse Empty Placeholder.
* Shows error or empty message. * Shows error or empty message.
* *
* @param state [BrowseState].
* @param isFilesEmpty Whether the list is empty. * @param isFilesEmpty Whether the list is empty.
* @param storagePermissionState Storage [PermissionState]. * @param storagePermissionState Storage [PermissionState].
* @param onNavigate Navigator callback.
* @param onEvent [BrowseEvent] callback.
*/ */
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@Composable @Composable
fun BoxScope.BrowseEmptyPlaceholder( fun BoxScope.BrowseEmptyPlaceholder(
state: State<BrowseState>,
isFilesEmpty: Boolean, isFilesEmpty: Boolean,
storagePermissionState: PermissionState, storagePermissionState: PermissionState
onNavigate: OnNavigate,
onEvent: (BrowseEvent) -> Unit
) { ) {
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
CustomAnimatedVisibility( CustomAnimatedVisibility(
visible = state.value.isError, visible = state.value.isError,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),

View file

@ -9,22 +9,22 @@ import androidx.compose.ui.res.stringResource
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState import com.google.accompanist.permissions.PermissionState
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
/** /**
* Storage permission dialog. * Storage permission dialog.
* *
* @param onEvent [BrowseEvent] callback.
* @param permissionState Storage [PermissionState]. * @param permissionState Storage [PermissionState].
*/ */
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@Composable @Composable
fun BrowseStoragePermissionDialog( fun BrowseStoragePermissionDialog(
onEvent: (BrowseEvent) -> Unit,
permissionState: PermissionState permissionState: PermissionState
) { ) {
val activity = LocalContext.current as ComponentActivity val activity = LocalContext.current as ComponentActivity
val onEvent = LocalBrowseViewModel.current.onEvent
CustomDialogWithContent( CustomDialogWithContent(
title = stringResource(id = R.string.storage_permission), title = stringResource(id = R.string.storage_permission),

View file

@ -9,7 +9,6 @@ import androidx.compose.material.icons.filled.AddChart
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
@ -18,30 +17,26 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.NullableBook import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/** /**
* Adding dialog. * Adding dialog.
* Adds all selected books to the Library. * Adds all selected books to the Library.
*
* @param state [BrowseState].
* @param onEvent [BrowseEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
*/ */
@Composable @Composable
fun BrowseAddingDialog( fun BrowseAddingDialog() {
state: State<BrowseState>,
onEvent: (BrowseEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.current val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
CustomDialogWithLazyColumn( CustomDialogWithLazyColumn(
title = stringResource(id = R.string.add_books), title = stringResource(id = R.string.add_books),
@ -54,7 +49,7 @@ fun BrowseAddingDialog(
onAction = { onAction = {
onEvent( onEvent(
BrowseEvent.OnAddBooks( BrowseEvent.OnAddBooks(
onNavigate = { navigator.it() }, onNavigate = onNavigate,
resetScroll = { resetScroll = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(0)) onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(0))
onLibraryEvent(LibraryEvent.OnLoadList) onLibraryEvent(LibraryEvent.OnLoadList)

View file

@ -8,14 +8,11 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.CustomBottomSheet import ua.acclorite.book_story.presentation.components.CustomBottomSheet
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseFilterSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseFilterSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseGeneralSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseGeneralSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseSortSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseSortSubcategory
@ -23,19 +20,12 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
/** /**
* Browse Filter Bottom Sheet. * Browse Filter Bottom Sheet.
* Lets user sort, filter and change appearance of the Browse. * Lets user sort, filter and change appearance of the Browse.
*
* @param state [BrowseState].
* @param mainState [MainState].
* @param onMainEvent [MainEvent] callback.
* @param onEvent [BrowseEvent] callback.
*/ */
@Composable @Composable
fun BrowseFilterBottomSheet( fun BrowseFilterBottomSheet() {
state: State<BrowseState>, val state = LocalBrowseViewModel.current.state
mainState: State<MainState>, val onEvent = LocalBrowseViewModel.current.onEvent
onMainEvent: (MainEvent) -> Unit,
onEvent: (BrowseEvent) -> Unit
) {
val pagerState = rememberPagerState(state.value.currentPage) { 3 } val pagerState = rememberPagerState(state.value.currentPage) { 3 }
LaunchedEffect(pagerState.currentPage) { LaunchedEffect(pagerState.currentPage) {
@ -67,8 +57,6 @@ fun BrowseFilterBottomSheet(
0 -> { 0 -> {
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
BrowseGeneralSubcategory( BrowseGeneralSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false, showTitle = false,
topPadding = 16.dp, topPadding = 16.dp,
bottomPadding = 8.dp + it bottomPadding = 8.dp + it
@ -79,8 +67,6 @@ fun BrowseFilterBottomSheet(
1 -> { 1 -> {
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
BrowseFilterSubcategory( BrowseFilterSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false, showTitle = false,
topPadding = 16.dp, topPadding = 16.dp,
bottomPadding = 8.dp + it bottomPadding = 8.dp + it
@ -91,8 +77,6 @@ fun BrowseFilterBottomSheet(
2 -> { 2 -> {
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
BrowseSortSubcategory( BrowseSortSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false, showTitle = false,
topPadding = 16.dp, topPadding = 16.dp,
bottomPadding = 8.dp + it bottomPadding = 8.dp + it

View file

@ -1,20 +1,16 @@
package ua.acclorite.book_story.presentation.screens.browse.components.layout package ua.acclorite.book_story.presentation.screens.browse.components.layout
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import ua.acclorite.book_story.domain.model.SelectableFile import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.presentation.data.MainState import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.screens.browse.components.layout.grid.BrowseGridLayout import ua.acclorite.book_story.presentation.screens.browse.components.layout.grid.BrowseGridLayout
import ua.acclorite.book_story.presentation.screens.browse.components.layout.list.BrowseListLayout import ua.acclorite.book_story.presentation.screens.browse.components.layout.list.BrowseListLayout
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
/** /**
* Browse Layout. * Browse Layout.
* Shows Grid or List. * Shows Grid or List.
* *
* @param state [BrowseState].
* @param mainState [MainState].
* @param filteredFiles Filtered items. * @param filteredFiles Filtered items.
* @param onLongItemClick OnLongItemClick callback. * @param onLongItemClick OnLongItemClick callback.
* @param onFavoriteItemClick OnFavoriteItemClick callback. * @param onFavoriteItemClick OnFavoriteItemClick callback.
@ -22,17 +18,16 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
*/ */
@Composable @Composable
fun BrowseLayout( fun BrowseLayout(
state: State<BrowseState>,
mainState: State<MainState>,
filteredFiles: List<SelectableFile>, filteredFiles: List<SelectableFile>,
onLongItemClick: (SelectableFile) -> Unit, onLongItemClick: (SelectableFile) -> Unit,
onFavoriteItemClick: (SelectableFile) -> Unit, onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit onItemClick: (SelectableFile) -> Unit
) { ) {
val mainState = LocalMainViewModel.current.state
when (mainState.value.browseLayout) { when (mainState.value.browseLayout) {
BrowseLayout.LIST -> { BrowseLayout.LIST -> {
BrowseListLayout( BrowseListLayout(
state = state,
filteredFiles = filteredFiles, filteredFiles = filteredFiles,
onLongItemClick = onLongItemClick, onLongItemClick = onLongItemClick,
onFavoriteItemClick = onFavoriteItemClick, onFavoriteItemClick = onFavoriteItemClick,
@ -42,7 +37,6 @@ fun BrowseLayout(
BrowseLayout.GRID -> { BrowseLayout.GRID -> {
BrowseGridLayout( BrowseGridLayout(
state = state,
gridSize = mainState.value.browseGridSize, gridSize = mainState.value.browseGridSize,
autoGridSize = mainState.value.browseAutoGridSize, autoGridSize = mainState.value.browseAutoGridSize,
filteredFiles = filteredFiles, filteredFiles = filteredFiles,

View file

@ -7,19 +7,17 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.model.SelectableFile import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.header import ua.acclorite.book_story.presentation.components.header
import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@Composable @Composable
fun BrowseGridLayout( fun BrowseGridLayout(
state: State<BrowseState>,
gridSize: Int, gridSize: Int,
autoGridSize: Boolean, autoGridSize: Boolean,
filteredFiles: List<SelectableFile>, filteredFiles: List<SelectableFile>,
@ -27,6 +25,8 @@ fun BrowseGridLayout(
onFavoriteItemClick: (SelectableFile) -> Unit, onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit, onItemClick: (SelectableFile) -> Unit,
) { ) {
val state = LocalBrowseViewModel.current.state
LazyVerticalGrid( LazyVerticalGrid(
columns = if (autoGridSize) GridCells.Adaptive(170.dp) columns = if (autoGridSize) GridCells.Adaptive(170.dp)
else GridCells.Fixed(gridSize.coerceAtLeast(1)), else GridCells.Fixed(gridSize.coerceAtLeast(1)),

View file

@ -5,23 +5,23 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.model.SelectableFile import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@Composable @Composable
fun BrowseListLayout( fun BrowseListLayout(
state: State<BrowseState>,
filteredFiles: List<SelectableFile>, filteredFiles: List<SelectableFile>,
onLongItemClick: (SelectableFile) -> Unit, onLongItemClick: (SelectableFile) -> Unit,
onFavoriteItemClick: (SelectableFile) -> Unit, onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit, onItemClick: (SelectableFile) -> Unit,
) { ) {
val state = LocalBrowseViewModel.current.state
LazyColumn( LazyColumn(
state = state.value.listState, state = state.value.listState,
modifier = Modifier modifier = Modifier

View file

@ -22,7 +22,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -39,10 +38,10 @@ import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomSearchTextField import ua.acclorite.book_story.presentation.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.MoreDropDown import ua.acclorite.book_story.presentation.components.MoreDropDown
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
@ -50,19 +49,15 @@ import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
/** /**
* Browse Top Bar. * Browse Top Bar.
* *
* @param state [BrowseState].
* @param mainState [MainState].
* @param filteredFiles Filtered files. * @param filteredFiles Filtered files.
* @param onEvent [BrowseEvent] callback.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun BrowseTopBar( fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
state: State<BrowseState>, val state = LocalBrowseViewModel.current.state
mainState: State<MainState>, val mainState = LocalMainViewModel.current.state
filteredFiles: List<SelectableFile>, val onEvent = LocalBrowseViewModel.current.onEvent
onEvent: (BrowseEvent) -> Unit
) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val selectedDirectoryName = remember { val selectedDirectoryName = remember {
mutableStateOf(state.value.selectedDirectory.name) mutableStateOf(state.value.selectedDirectory.name)
@ -245,11 +240,7 @@ fun BrowseTopBar(
}, },
customContent = { customContent = {
BrowseTopBarDirectoryPath( BrowseTopBarDirectoryPath()
state = state,
mainState = mainState,
onEvent = onEvent
)
} }
) )
} }

View file

@ -19,7 +19,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -28,20 +27,20 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.customItemsIndexed import ua.acclorite.book_story.presentation.components.customItemsIndexed
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
import java.io.File import java.io.File
@Composable @Composable
fun BrowseTopBarDirectoryPath( fun BrowseTopBarDirectoryPath() {
state: State<BrowseState>, val state = LocalBrowseViewModel.current.state
mainState: State<MainState>, val mainState = LocalMainViewModel.current.state
onEvent: (BrowseEvent) -> Unit val onEvent = LocalBrowseViewModel.current.onEvent
) {
val context = LocalContext.current val context = LocalContext.current
val rootDirectory = remember { val rootDirectory = remember {
Environment.getExternalStorageDirectory() Environment.getExternalStorageDirectory()
} }

View file

@ -9,7 +9,6 @@ import android.os.Environment
import android.provider.Settings import android.provider.Settings
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.isGranted
@ -29,6 +28,7 @@ import ua.acclorite.book_story.domain.use_case.GetBookFromFile
import ua.acclorite.book_story.domain.use_case.GetFilesFromDevice import ua.acclorite.book_story.domain.use_case.GetFilesFromDevice
import ua.acclorite.book_story.domain.use_case.InsertBook import ua.acclorite.book_story.domain.use_case.InsertBook
import ua.acclorite.book_story.domain.use_case.UpdateFavoriteDirectory import ua.acclorite.book_story.domain.use_case.UpdateFavoriteDirectory
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.presentation.data.MainState import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.launchActivity import ua.acclorite.book_story.presentation.data.launchActivity
@ -43,10 +43,10 @@ class BrowseViewModel @Inject constructor(
private val getFilesFromDevice: GetFilesFromDevice, private val getFilesFromDevice: GetFilesFromDevice,
private val insertBook: InsertBook, private val insertBook: InsertBook,
private val updateFavoriteDirectory: UpdateFavoriteDirectory, private val updateFavoriteDirectory: UpdateFavoriteDirectory,
) : ViewModel() { ) : BaseViewModel<BrowseState, BrowseEvent>() {
private val _state = MutableStateFlow(BrowseState()) private val _state = MutableStateFlow(BrowseState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private var searchQueryJob: Job? = null private var searchQueryJob: Job? = null
private var refreshListJob: Job? = null private var refreshListJob: Job? = null
@ -67,7 +67,7 @@ class BrowseViewModel @Inject constructor(
} }
} }
fun onEvent(event: BrowseEvent) { override fun onEvent(event: BrowseEvent) {
when (event) { when (event) {
is BrowseEvent.OnStoragePermissionRequest -> { is BrowseEvent.OnStoragePermissionRequest -> {
val legacyStoragePermission = Build.VERSION.SDK_INT < Build.VERSION_CODES.R val legacyStoragePermission = Build.VERSION.SDK_INT < Build.VERSION_CODES.R

View file

@ -22,68 +22,51 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalHelpViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.LocalStartViewModel
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Screen 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.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.help.components.HelpItem import ua.acclorite.book_story.presentation.screens.help.components.HelpItem
import ua.acclorite.book_story.presentation.screens.help.components.items.HelpClickMeNoteItem import ua.acclorite.book_story.presentation.screens.help.components.items.HelpClickMeNoteItem
import ua.acclorite.book_story.presentation.screens.help.data.HelpState
import ua.acclorite.book_story.presentation.screens.help.data.HelpViewModel
import ua.acclorite.book_story.presentation.screens.start.data.StartEvent import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
import ua.acclorite.book_story.presentation.screens.start.data.StartViewModel
@Composable @Composable
fun HelpScreenRoot(screen: Screen.Help) { fun HelpScreenRoot(screen: Screen.Help) {
val navigator = LocalNavigator.current val viewModel = LocalHelpViewModel.current.viewModel
val helpViewModel: HelpViewModel = hiltViewModel()
val browseViewModel: BrowseViewModel = hiltViewModel()
val mainViewModel: MainViewModel = hiltViewModel()
val startViewModel: StartViewModel = hiltViewModel()
val state = helpViewModel.state.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
helpViewModel.init(screen = screen) viewModel.init(screen = screen)
} }
HelpScreen( HelpScreen()
state = state,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
onStartEvent = startViewModel::onEvent
)
} }
@OptIn( @OptIn(
ExperimentalMaterial3Api::class ExperimentalMaterial3Api::class
) )
@Composable @Composable
private fun HelpScreen( private fun HelpScreen() {
state: State<HelpState>, val state = LocalHelpViewModel.current.state
onNavigate: OnNavigate, val onMainEvent = LocalMainViewModel.current.onEvent
onMainEvent: (MainEvent) -> Unit, val onBrowseEvent = LocalBrowseViewModel.current.onEvent
onBrowseEvent: (BrowseEvent) -> Unit, val onStartEvent = LocalStartViewModel.current.onEvent
onStartEvent: (StartEvent) -> Unit val onNavigate = LocalOnNavigate.current
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
@ -171,7 +154,6 @@ private fun HelpScreen(
customItems(Constants.HELP_TIPS, key = { it.title }) { helpTip -> customItems(Constants.HELP_TIPS, key = { it.title }) { helpTip ->
HelpItem( HelpItem(
helpTip = helpTip, helpTip = helpTip,
onNavigate = onNavigate,
fromStart = state.value.fromStart fromStart = state.value.fromStart
) )
} }

View file

@ -31,7 +31,7 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.HelpTip import ua.acclorite.book_story.domain.model.HelpTip
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.ui.ExpandingTransition import ua.acclorite.book_story.presentation.ui.ExpandingTransition
/** /**
@ -39,15 +39,15 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
* Can be Expanded or Collapsed. * Can be Expanded or Collapsed.
* *
* @param helpTip [HelpTip] to be shown. * @param helpTip [HelpTip] to be shown.
* @param onNavigate Navigator callback.
* @param fromStart Whether user came from Start screen. * @param fromStart Whether user came from Start screen.
*/ */
@Composable @Composable
fun HelpItem( fun HelpItem(
helpTip: HelpTip, helpTip: HelpTip,
onNavigate: OnNavigate,
fromStart: Boolean fromStart: Boolean
) { ) {
val onNavigate = LocalOnNavigate.current
val showDescription = rememberSaveable { val showDescription = rememberSaveable {
mutableStateOf(false) mutableStateOf(false)
} }

View file

@ -0,0 +1,6 @@
package ua.acclorite.book_story.presentation.screens.help.data
import androidx.compose.runtime.Immutable
@Immutable
sealed class HelpEvent

View file

@ -1,22 +1,24 @@
package ua.acclorite.book_story.presentation.screens.help.data package ua.acclorite.book_story.presentation.screens.help.data
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class HelpViewModel @Inject constructor( class HelpViewModel @Inject constructor(
) : ViewModel() { ) : BaseViewModel<HelpState, HelpEvent>() {
private val _state = MutableStateFlow(HelpState()) private val _state = MutableStateFlow(HelpState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
override fun onEvent(event: HelpEvent) {}
fun init(screen: Screen.Help) { fun init(screen: Screen.Help) {
viewModelScope.launch { viewModelScope.launch {

View file

@ -24,8 +24,6 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -36,44 +34,36 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R 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.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomSearchTextField import ua.acclorite.book_story.presentation.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.components.CustomSnackbar import ua.acclorite.book_story.presentation.components.CustomSnackbar
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.MoreDropDown import ua.acclorite.book_story.presentation.components.MoreDropDown
import ua.acclorite.book_story.presentation.components.customItems 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.IsEmpty
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen 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.HistoryDeleteWholeHistoryDialog
import ua.acclorite.book_story.presentation.screens.history.components.HistoryItem import ua.acclorite.book_story.presentation.screens.history.components.HistoryItem
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.history.data.HistoryState
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.ui.DefaultTransition import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions import ua.acclorite.book_story.presentation.ui.Transitions
@Composable @Composable
fun HistoryScreenRoot() { fun HistoryScreenRoot() {
val navigator = LocalNavigator.current val onEvent = LocalHistoryViewModel.current.onEvent
val viewModel: HistoryViewModel = hiltViewModel()
val libraryViewModel: LibraryViewModel = hiltViewModel()
val state = viewModel.state.collectAsState() LaunchedEffect(Unit) {
onEvent(HistoryEvent.OnUpdateScrollOffset)
}
HistoryScreen( HistoryScreen()
state = state,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
)
} }
@OptIn( @OptIn(
@ -81,13 +71,13 @@ fun HistoryScreenRoot() {
ExperimentalMaterialApi::class ExperimentalMaterialApi::class
) )
@Composable @Composable
private fun HistoryScreen( private fun HistoryScreen() {
state: State<HistoryState>,
onNavigate: OnNavigate,
onEvent: (HistoryEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val state = LocalHistoryViewModel.current.state
val onEvent = LocalHistoryViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
refreshing = state.value.isRefreshing, refreshing = state.value.isRefreshing,
onRefresh = { onRefresh = {
@ -97,15 +87,8 @@ private fun HistoryScreen(
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val snackbarState = remember { SnackbarHostState() } val snackbarState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
onEvent(HistoryEvent.OnUpdateScrollOffset)
}
if (state.value.showDeleteWholeHistoryDialog) { if (state.value.showDeleteWholeHistoryDialog) {
HistoryDeleteWholeHistoryDialog( HistoryDeleteWholeHistoryDialog()
onEvent = onEvent,
onLibraryLoadEvent = onLibraryEvent
)
} }
Scaffold( Scaffold(
@ -303,16 +286,4 @@ private fun HistoryScreen(
navigate(Screen.Library) navigate(Screen.Library)
} }
} }
} }

View file

@ -6,6 +6,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
@ -15,11 +17,10 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
* Delete whole history dialog. * Delete whole history dialog.
*/ */
@Composable @Composable
fun HistoryDeleteWholeHistoryDialog( fun HistoryDeleteWholeHistoryDialog() {
onEvent: (HistoryEvent) -> Unit,
onLibraryLoadEvent: (LibraryEvent.OnLoadList) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val onEvent = LocalHistoryViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
CustomDialogWithContent( CustomDialogWithContent(
title = stringResource(id = R.string.delete_history), title = stringResource(id = R.string.delete_history),
@ -34,7 +35,7 @@ fun HistoryDeleteWholeHistoryDialog(
onAction = { onAction = {
onEvent( onEvent(
HistoryEvent.OnDeleteWholeHistory { HistoryEvent.OnDeleteWholeHistory {
onLibraryLoadEvent(LibraryEvent.OnLoadList) onLibraryEvent(LibraryEvent.OnLoadList)
} }
) )
context.getString(R.string.history_deleted) context.getString(R.string.history_deleted)

View file

@ -2,7 +2,6 @@ package ua.acclorite.book_story.presentation.screens.history.data
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -21,6 +20,7 @@ import ua.acclorite.book_story.domain.use_case.DeleteWholeHistory
import ua.acclorite.book_story.domain.use_case.GetBooksById 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.GetHistory
import ua.acclorite.book_story.domain.use_case.InsertHistory import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.Resource import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -36,10 +36,10 @@ class HistoryViewModel @Inject constructor(
private val deleteWholeHistory: DeleteWholeHistory, private val deleteWholeHistory: DeleteWholeHistory,
private val deleteHistory: DeleteHistory, private val deleteHistory: DeleteHistory,
private val getBooksById: GetBooksById private val getBooksById: GetBooksById
) : ViewModel() { ) : BaseViewModel<HistoryState, HistoryEvent>() {
private val _state = MutableStateFlow(HistoryState()) private val _state = MutableStateFlow(HistoryState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private var job: Job? = null private var job: Job? = null
private var job2: Job? = null private var job2: Job? = null
@ -56,7 +56,7 @@ class HistoryViewModel @Inject constructor(
} }
} }
fun onEvent(event: HistoryEvent) { override fun onEvent(event: HistoryEvent) {
when (event) { when (event) {
is HistoryEvent.OnRefreshList -> { is HistoryEvent.OnRefreshList -> {
job3?.cancel() job3?.cancel()

View file

@ -27,8 +27,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -41,47 +39,33 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Book import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category 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.domain.util.Selected
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.header import ua.acclorite.book_story.presentation.components.header
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
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.components.LibraryBookItem import ua.acclorite.book_story.presentation.screens.library.components.LibraryBookItem
import ua.acclorite.book_story.presentation.screens.library.components.LibraryTopBar import ua.acclorite.book_story.presentation.screens.library.components.LibraryTopBar
import ua.acclorite.book_story.presentation.screens.library.components.dialog.LibraryDeleteDialog import ua.acclorite.book_story.presentation.screens.library.components.dialog.LibraryDeleteDialog
import ua.acclorite.book_story.presentation.screens.library.components.dialog.LibraryMoveDialog import ua.acclorite.book_story.presentation.screens.library.components.dialog.LibraryMoveDialog
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.ui.DefaultTransition import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions import ua.acclorite.book_story.presentation.ui.Transitions
@Composable @Composable
fun LibraryScreenRoot() { fun LibraryScreenRoot() {
val navigator = LocalNavigator.current val state = LocalLibraryViewModel.current.state
val viewModel: LibraryViewModel = hiltViewModel() val onEvent = LocalLibraryViewModel.current.onEvent
val mainViewModel: MainViewModel = hiltViewModel()
val browseViewModel: BrowseViewModel = hiltViewModel()
val historyViewModel: HistoryViewModel = hiltViewModel()
val state = viewModel.state.collectAsState()
val mainState = mainViewModel.state.collectAsState()
val pagerState = rememberPagerState( val pagerState = rememberPagerState(
initialPage = state.value.currentPage, initialPage = state.value.currentPage,
@ -90,60 +74,43 @@ fun LibraryScreenRoot() {
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
refreshing = state.value.isRefreshing, refreshing = state.value.isRefreshing,
onRefresh = { onRefresh = {
viewModel.onEvent(LibraryEvent.OnRefreshList) onEvent(LibraryEvent.OnRefreshList)
} }
) )
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.onEvent(LibraryEvent.OnScrollToPage(state.value.currentPage, pagerState)) onEvent(LibraryEvent.OnScrollToPage(state.value.currentPage, pagerState))
} }
LaunchedEffect(pagerState.currentPage) { LaunchedEffect(pagerState.currentPage) {
if (pagerState.currentPage != state.value.currentPage) { if (pagerState.currentPage != state.value.currentPage) {
viewModel.onEvent(LibraryEvent.OnUpdateCurrentPage(pagerState.currentPage)) onEvent(LibraryEvent.OnUpdateCurrentPage(pagerState.currentPage))
} }
} }
LibraryScreen( LibraryScreen(
state = state,
mainState = mainState,
onNavigate = { navigator.it() },
pagerState = pagerState, pagerState = pagerState,
refreshState = refreshState, refreshState = refreshState
onEvent = viewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
onHistoryEvent = historyViewModel::onEvent
) )
} }
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
private fun LibraryScreen( private fun LibraryScreen(
state: State<LibraryState>,
mainState: State<MainState>,
onNavigate: OnNavigate,
pagerState: PagerState, pagerState: PagerState,
refreshState: PullRefreshState, refreshState: PullRefreshState
onEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit
) { ) {
val state = LocalLibraryViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
var isScrollInProgress by remember { mutableStateOf(false) } var isScrollInProgress by remember { mutableStateOf(false) }
if (state.value.showMoveDialog) { if (state.value.showMoveDialog) {
LibraryMoveDialog( LibraryMoveDialog(pagerState = pagerState)
state = state,
onEvent = onEvent,
onHistoryLoadEvent = onHistoryEvent,
pagerState = pagerState
)
} }
if (state.value.showDeleteDialog) { if (state.value.showDeleteDialog) {
LibraryDeleteDialog( LibraryDeleteDialog()
state = state,
onEvent = onEvent,
onBrowseLoadEvent = onBrowseEvent,
onHistoryLoadEvent = onHistoryEvent
)
} }
Scaffold( Scaffold(
@ -152,11 +119,7 @@ private fun LibraryScreen(
.pullRefresh(refreshState), .pullRefresh(refreshState),
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
topBar = { topBar = {
LibraryTopBar( LibraryTopBar(pagerState = pagerState)
state = state,
pagerState = pagerState,
onEvent = onEvent
)
} }
) { paddingValues -> ) { paddingValues ->
Box( Box(

View file

@ -20,7 +20,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -35,25 +34,21 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomSearchTextField import ua.acclorite.book_story.presentation.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.MoreDropDown import ua.acclorite.book_story.presentation.components.MoreDropDown
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/** /**
* Library Top Bar. * Library Top Bar.
* Contains [AnimatedTopAppBar] and [LibraryTabRow]. * Contains [AnimatedTopAppBar] and [LibraryTabRow].
* *
* @param state [LibraryState].
* @param pagerState [PagerState]. * @param pagerState [PagerState].
* @param onEvent [LibraryEvent] callback.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun LibraryTopBar( fun LibraryTopBar(pagerState: PagerState) {
state: State<LibraryState>, val state = LocalLibraryViewModel.current.state
pagerState: PagerState, val onEvent = LocalLibraryViewModel.current.onEvent
onEvent: (LibraryEvent) -> Unit
) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
AnimatedTopAppBar( AnimatedTopAppBar(

View file

@ -3,28 +3,28 @@ package ua.acclorite.book_story.presentation.screens.library.components.dialog
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DeleteOutline import androidx.compose.material.icons.outlined.DeleteOutline
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/** /**
* Delete dialog. Deletes all selected books. * Delete dialog. Deletes all selected books.
*/ */
@Composable @Composable
fun LibraryDeleteDialog( fun LibraryDeleteDialog() {
state: State<LibraryState>,
onEvent: (LibraryEvent) -> Unit,
onBrowseLoadEvent: (BrowseEvent.OnLoadList) -> Unit,
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val onBrowseEvent = LocalBrowseViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
CustomDialogWithContent( CustomDialogWithContent(
title = stringResource(id = R.string.delete_books), title = stringResource(id = R.string.delete_books),
@ -40,8 +40,8 @@ fun LibraryDeleteDialog(
onAction = { onAction = {
onEvent( onEvent(
LibraryEvent.OnDeleteBooks { LibraryEvent.OnDeleteBooks {
onBrowseLoadEvent(BrowseEvent.OnLoadList) onBrowseEvent(BrowseEvent.OnLoadList)
onHistoryLoadEvent(HistoryEvent.OnLoadList) onHistoryEvent(HistoryEvent.OnLoadList)
} }
) )
context.getString(R.string.books_deleted) context.getString(R.string.books_deleted)

View file

@ -4,30 +4,28 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.DriveFileMove import androidx.compose.material.icons.automirrored.outlined.DriveFileMove
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Category import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.components.custom_dialog.SelectableDialogItem import ua.acclorite.book_story.presentation.components.custom_dialog.SelectableDialogItem
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/** /**
* Move dialog. Moves all selected books to the selected category. * Move dialog. Moves all selected books to the selected category.
*/ */
@Composable @Composable
fun LibraryMoveDialog( fun LibraryMoveDialog(pagerState: PagerState) {
state: State<LibraryState>,
onEvent: (LibraryEvent) -> Unit,
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit,
pagerState: PagerState
) {
val context = LocalContext.current val context = LocalContext.current
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
CustomDialogWithLazyColumn( CustomDialogWithLazyColumn(
title = stringResource(id = R.string.move_books), title = stringResource(id = R.string.move_books),
@ -44,7 +42,7 @@ fun LibraryMoveDialog(
LibraryEvent.OnMoveBooks( LibraryEvent.OnMoveBooks(
pagerState, pagerState,
refreshList = { refreshList = {
onHistoryLoadEvent(HistoryEvent.OnLoadList) onHistoryEvent(HistoryEvent.OnLoadList)
} }
) )
) )

View file

@ -1,6 +1,5 @@
package ua.acclorite.book_story.presentation.screens.library.data package ua.acclorite.book_story.presentation.screens.library.data
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -18,6 +17,7 @@ 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.GetBooks
import ua.acclorite.book_story.domain.use_case.InsertHistory import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.use_case.UpdateBooks import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import java.util.Date import java.util.Date
import javax.inject.Inject import javax.inject.Inject
@ -28,10 +28,10 @@ class LibraryViewModel @Inject constructor(
private val updateBooks: UpdateBooks, private val updateBooks: UpdateBooks,
private val deleteBooks: DeleteBooks, private val deleteBooks: DeleteBooks,
private val insertHistory: InsertHistory private val insertHistory: InsertHistory
) : ViewModel() { ) : BaseViewModel<LibraryState, LibraryEvent>() {
private val _state = MutableStateFlow(LibraryState()) private val _state = MutableStateFlow(LibraryState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private val _isReady = MutableStateFlow(false) private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow() val isReady = _isReady.asStateFlow()
@ -53,7 +53,7 @@ class LibraryViewModel @Inject constructor(
} }
} }
fun onEvent(event: LibraryEvent) { override fun onEvent(event: LibraryEvent) {
when (event) { when (event) {
is LibraryEvent.OnScrollToPage -> { is LibraryEvent.OnScrollToPage -> {
viewModelScope.launch { viewModelScope.launch {

View file

@ -26,8 +26,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -49,24 +47,22 @@ import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants 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.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomSelectionContainer import ua.acclorite.book_story.presentation.components.CustomSelectionContainer
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.components.customItemsIndexed import ua.acclorite.book_story.presentation.components.customItemsIndexed
import ua.acclorite.book_story.presentation.components.is_messages.IsError 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.LocalOnNavigate
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.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent 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 import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.screens.reader.components.ReaderEndItem import ua.acclorite.book_story.presentation.screens.reader.components.ReaderEndItem
import ua.acclorite.book_story.presentation.screens.reader.components.ReaderTextParagraph import ua.acclorite.book_story.presentation.screens.reader.components.ReaderTextParagraph
import ua.acclorite.book_story.presentation.screens.reader.components.app_bar.ReaderBottomBar import ua.acclorite.book_story.presentation.screens.reader.components.app_bar.ReaderBottomBar
@ -75,26 +71,16 @@ import ua.acclorite.book_story.presentation.screens.reader.components.readerFast
import ua.acclorite.book_story.presentation.screens.reader.components.settings_bottom_sheet.ReaderSettingsBottomSheet import ua.acclorite.book_story.presentation.screens.reader.components.settings_bottom_sheet.ReaderSettingsBottomSheet
import ua.acclorite.book_story.presentation.screens.reader.components.start_item.ReaderStartItem import ua.acclorite.book_story.presentation.screens.reader.components.start_item.ReaderStartItem
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
@Composable @Composable
fun ReaderScreenRoot(screen: Screen.Reader) { fun ReaderScreenRoot(screen: Screen.Reader) {
val navigator = LocalNavigator.current val state = LocalReaderViewModel.current.state
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val viewModel = LocalReaderViewModel.current.viewModel
val context = LocalContext.current as ComponentActivity val context = LocalContext.current as ComponentActivity
val onNavigate = LocalOnNavigate.current
val viewModel: ReaderViewModel = hiltViewModel()
val mainViewModel: MainViewModel = hiltViewModel()
val libraryViewModel: LibraryViewModel = hiltViewModel()
val historyViewModel: HistoryViewModel = hiltViewModel()
val settingsViewModel: SettingsViewModel = hiltViewModel()
val state = viewModel.state.collectAsState()
val mainState = mainViewModel.state.collectAsState()
val settingsState = settingsViewModel.state.collectAsState()
val lazyListState = rememberSaveable( val lazyListState = rememberSaveable(
state.value.listState, state.value.listState,
@ -111,11 +97,11 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.init( viewModel.init(
screen = screen, screen = screen,
onNavigate = { navigator.it() }, onNavigate = onNavigate,
context = context, context = context,
refreshList = { refreshList = {
libraryViewModel.onEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
historyViewModel.onEvent(HistoryEvent.OnLoadList) onHistoryEvent(HistoryEvent.OnLoadList)
}, },
onError = { onError = {
it.asString(context) it.asString(context)
@ -129,12 +115,14 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
} }
} }
LaunchedEffect(lazyListState) { LaunchedEffect(lazyListState) {
viewModel.onUpdateProgress( viewModel.onUpdateProgress {
onLibraryEvent = libraryViewModel::onEvent, onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent = historyViewModel::onEvent onHistoryEvent(HistoryEvent.OnUpdateBook(it))
) }
} }
ReaderScreen(lazyListState = lazyListState)
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { onDispose {
viewModel.clearViewModel() viewModel.clearViewModel()
@ -145,36 +133,19 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
context.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) context.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }
} }
ReaderScreen(
state = state,
mainState = mainState,
settingsState = settingsState,
lazyListState = lazyListState,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onMainEvent = mainViewModel::onEvent,
onSettingsEvent = settingsViewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent,
onHistoryEvent = historyViewModel::onEvent
)
} }
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable @Composable
private fun ReaderScreen( private fun ReaderScreen(lazyListState: LazyListState) {
state: State<ReaderState>, val state = LocalReaderViewModel.current.state
mainState: State<MainState>, val mainState = LocalMainViewModel.current.state
settingsState: State<SettingsState>, val settingsState = LocalSettingsViewModel.current.state
lazyListState: LazyListState, val onEvent = LocalReaderViewModel.current.onEvent
onNavigate: OnNavigate, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onEvent: (ReaderEvent) -> Unit, val onHistoryEvent = LocalHistoryViewModel.current.onEvent
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
) {
val context = LocalContext.current as ComponentActivity val context = LocalContext.current as ComponentActivity
val onNavigate = LocalOnNavigate.current
val nestedScrollConnection = remember(state) { val nestedScrollConnection = remember(state) {
object : NestedScrollConnection { object : NestedScrollConnection {
@ -230,13 +201,7 @@ private fun ReaderScreen(
} }
if (state.value.showSettingsBottomSheet) { if (state.value.showSettingsBottomSheet) {
ReaderSettingsBottomSheet( ReaderSettingsBottomSheet()
mainState = mainState,
settingsState = settingsState,
onEvent = onEvent,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent
)
} }
Scaffold( Scaffold(
@ -250,13 +215,7 @@ private fun ReaderScreen(
enter = slideInVertically { -it }, enter = slideInVertically { -it },
exit = slideOutVertically { -it } exit = slideOutVertically { -it }
) { ) {
ReaderTopBar( ReaderTopBar()
state = state,
onNavigate = onNavigate,
onEvent = onEvent,
onLibraryUpdateEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
)
} }
}, },
bottomBar = { bottomBar = {
@ -266,12 +225,7 @@ private fun ReaderScreen(
enter = slideInVertically { it }, enter = slideInVertically { it },
exit = slideOutVertically { it } exit = slideOutVertically { it }
) { ) {
ReaderBottomBar( ReaderBottomBar()
state = state,
onEvent = onEvent,
onLibraryUpdateEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent
)
} }
} }
) { ) {
@ -357,7 +311,6 @@ private fun ReaderScreen(
fastColorPresetChangeEnabled = mainState.value.fastColorPresetChange, fastColorPresetChangeEnabled = mainState.value.fastColorPresetChange,
isLoading = state.value.loading, isLoading = state.value.loading,
toolbarHidden = toolbarHidden, toolbarHidden = toolbarHidden,
onSettingsEvent = onSettingsEvent,
presetChanged = { presetChanged = {
context context
.getString( .getString(
@ -373,7 +326,7 @@ private fun ReaderScreen(
) { ) {
item { item {
DisableSelection { DisableSelection {
ReaderStartItem(state = state) ReaderStartItem()
} }
Spacer(modifier = Modifier.height(18.dp)) Spacer(modifier = Modifier.height(18.dp))
@ -395,8 +348,7 @@ private fun ReaderScreen(
sidePadding = sidePadding, sidePadding = sidePadding,
paragraphIndentation = mainState.value.paragraphIndentation, paragraphIndentation = mainState.value.paragraphIndentation,
doubleClickTranslationEnabled = mainState.value.doubleClickTranslation, doubleClickTranslationEnabled = mainState.value.doubleClickTranslation,
toolbarHidden = toolbarHidden, toolbarHidden = toolbarHidden
onEvent = onEvent
) )
} }
@ -404,13 +356,7 @@ private fun ReaderScreen(
Spacer(modifier = Modifier.height(18.dp)) Spacer(modifier = Modifier.height(18.dp))
DisableSelection { DisableSelection {
ReaderEndItem( ReaderEndItem()
state = state,
onNavigate = onNavigate,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent,
onHistoryUpdateEvent = onHistoryEvent,
)
} }
} }
} }

View file

@ -17,7 +17,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -29,33 +28,30 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Category import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
/** /**
* Reader end item. Displays at the end of the book. * 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.
*/ */
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun ReaderEndItem( fun ReaderEndItem(
state: State<ReaderState>,
onNavigate: OnNavigate,
onEvent: (ReaderEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit,
) { ) {
val state = LocalReaderViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val context = LocalContext.current as ComponentActivity val context = LocalContext.current as ComponentActivity
val onNavigate = LocalOnNavigate.current
val buttonText = remember { val buttonText = remember {
context.getString( context.getString(
if (state.value.book.category != Category.ALREADY_READ) R.string.move_to_read if (state.value.book.category != Category.ALREADY_READ) R.string.move_to_read
@ -102,7 +98,7 @@ fun ReaderEndItem(
context = context, context = context,
onUpdateCategories = { onUpdateCategories = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
updatePage = { updatePage = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it)) onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it))
@ -119,7 +115,7 @@ fun ReaderEndItem(
context = context, context = context,
refreshList = { refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
navigate = { navigate = {
onNavigate { onNavigate {

View file

@ -7,6 +7,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import ua.acclorite.book_story.domain.util.UIText import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
/** /**
@ -16,7 +17,6 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
* @param fastColorPresetChangeEnabled Whether this is enabled. * @param fastColorPresetChangeEnabled Whether this is enabled.
* @param isLoading Whether Reader is loading. * @param isLoading Whether Reader is loading.
* @param toolbarHidden Whether toolbar(selection) is hidden. * @param toolbarHidden Whether toolbar(selection) is hidden.
* @param onSettingsEvent [SettingsEvent] callback.
* @param presetChanged Callback when Color Preset was changed. * @param presetChanged Callback when Color Preset was changed.
*/ */
@Composable @Composable
@ -24,9 +24,10 @@ fun Modifier.readerFastColorPresetChange(
fastColorPresetChangeEnabled: Boolean, fastColorPresetChangeEnabled: Boolean,
isLoading: Boolean, isLoading: Boolean,
toolbarHidden: Boolean, toolbarHidden: Boolean,
onSettingsEvent: (SettingsEvent) -> Unit,
presetChanged: (UIText) -> Unit presetChanged: (UIText) -> Unit
): Modifier { ): Modifier {
val onSettingsEvent = LocalSettingsViewModel.current.onEvent
val offset = remember { mutableFloatStateOf(0f) } val offset = remember { mutableFloatStateOf(0f) }
return this.then( return this.then(
if (fastColorPresetChangeEnabled && toolbarHidden && !isLoading) { if (fastColorPresetChangeEnabled && toolbarHidden && !isLoading) {

View file

@ -23,6 +23,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.FontWithName import ua.acclorite.book_story.domain.model.FontWithName
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.data.showToast import ua.acclorite.book_story.presentation.data.showToast
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.ReaderTextAlignment import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.ReaderTextAlignment
@ -43,7 +44,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.
* @param paragraphIndentation Whether Paragraph Indentation is enabled for this line. * @param paragraphIndentation Whether Paragraph Indentation is enabled for this line.
* @param doubleClickTranslationEnabled Whether Double Click Translation is enabled. * @param doubleClickTranslationEnabled Whether Double Click Translation is enabled.
* @param toolbarHidden Whether selection toolBar is hidden. * @param toolbarHidden Whether selection toolBar is hidden.
* @param onEvent [ReaderEvent] callback.
*/ */
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -60,9 +60,10 @@ fun LazyItemScope.ReaderTextParagraph(
sidePadding: Dp, sidePadding: Dp,
paragraphIndentation: Boolean, paragraphIndentation: Boolean,
doubleClickTranslationEnabled: Boolean, doubleClickTranslationEnabled: Boolean,
toolbarHidden: Boolean, toolbarHidden: Boolean
onEvent: (ReaderEvent) -> Unit
) { ) {
val onEvent = LocalReaderViewModel.current.onEvent
Column( Column(
modifier = Modifier modifier = Modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null) .animateItem(fadeInSpec = null, fadeOutSpec = null)

View file

@ -14,36 +14,32 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.data.removeDigits import ua.acclorite.book_story.presentation.data.removeDigits
import ua.acclorite.book_story.presentation.data.removeTrailingZero import ua.acclorite.book_story.presentation.data.removeTrailingZero
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.ui.Colors import ua.acclorite.book_story.presentation.ui.Colors
/** /**
* Reader bottom bar. Has a slider to change progress. * Reader bottom bar. Has a slider to change progress.
*
* @param state [ReaderState].
* @param onEvent [ReaderEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/ */
@Composable @Composable
fun ReaderBottomBar( fun ReaderBottomBar() {
state: State<ReaderState>, val state = LocalReaderViewModel.current.state
onEvent: (ReaderEvent) -> Unit, val onEvent = LocalReaderViewModel.current.onEvent
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit val onHistoryEvent = LocalHistoryViewModel.current.onEvent
) {
val progress by remember(state.value.book.progress) { val progress by remember(state.value.book.progress) {
derivedStateOf { derivedStateOf {
(state.value.book.progress * 100) (state.value.book.progress * 100)
@ -87,8 +83,8 @@ fun ReaderBottomBar(
firstVisibleItemIndex = state.value.listState.firstVisibleItemIndex, firstVisibleItemIndex = state.value.listState.firstVisibleItemIndex,
firstVisibleItemOffset = 0, firstVisibleItemOffset = 0,
refreshList = { book -> refreshList = { book ->
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(book)) onLibraryEvent(LibraryEvent.OnUpdateBook(book))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(book)) onHistoryEvent(HistoryEvent.OnUpdateBook(book))
} }
) )
) )

View file

@ -14,7 +14,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
@ -22,34 +21,29 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import ua.acclorite.book_story.R 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.CustomIconButton
import ua.acclorite.book_story.presentation.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen 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.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.ui.Colors import ua.acclorite.book_story.presentation.ui.Colors
/** /**
* Reader top bar. Displays title of the book. * 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.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ReaderTopBar( fun ReaderTopBar() {
state: State<ReaderState>, val state = LocalReaderViewModel.current.state
onNavigate: OnNavigate, val onEvent = LocalReaderViewModel.current.onEvent
onEvent: (ReaderEvent) -> Unit, val onLibraryEvent = LocalLibraryViewModel.current.onEvent
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit, val onHistoryEvent = LocalHistoryViewModel.current.onEvent
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
val context = LocalContext.current as ComponentActivity val context = LocalContext.current as ComponentActivity
val onNavigate = LocalOnNavigate.current
TopAppBar( TopAppBar(
navigationIcon = { navigationIcon = {
@ -62,8 +56,8 @@ fun ReaderTopBar(
ReaderEvent.OnGoBack( ReaderEvent.OnGoBack(
context = context, context = context,
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
navigate = { navigate = {
onNavigate { onNavigate {
@ -94,8 +88,8 @@ fun ReaderTopBar(
ReaderEvent.OnGoBack( ReaderEvent.OnGoBack(
context = context, context = context,
refreshList = { refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it)) onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it)) onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}, },
navigate = { navigate = {
onNavigate { onNavigate {

View file

@ -14,67 +14,49 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.CustomBottomSheet import ua.acclorite.book_story.presentation.components.CustomBottomSheet
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ColorsSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ColorsSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.ReaderSettingsCategory import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.ReaderSettingsCategory
/** /**
* Settings bottom sheet. Has General and Colors categories. * Settings bottom sheet. Has General and Colors categories.
*
* @param mainState [MainState].
* @param settingsState [SettingsState].
* @param onEvent [ReaderEvent] callback.
* @param onMainEvent [MainEvent] callback.
* @param onSettingsEvent [SettingsEvent] callback.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ReaderSettingsBottomSheet( fun ReaderSettingsBottomSheet() {
mainState: State<MainState>, val state = LocalReaderViewModel.current.state
settingsState: State<SettingsState>, val onEvent = LocalReaderViewModel.current.onEvent
onEvent: (ReaderEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit
) {
val pagerState = rememberPagerState { 2 }
val currentPage by remember { derivedStateOf { pagerState.currentPage } }
val context = LocalContext.current val context = LocalContext.current
val scrimColor = if (currentPage == 1) Color.Transparent val pagerState = rememberPagerState(state.value.currentPage) { 3 }
else BottomSheetDefaults.ScrimColor
val animatedScrimColor by animateColorAsState( val animatedScrimColor by animateColorAsState(
targetValue = scrimColor, targetValue = if (pagerState.currentPage == 2) Color.Transparent
animationSpec = tween(300), else BottomSheetDefaults.ScrimColor,
label = "Scrim animation" animationSpec = tween(300)
) )
val height = remember(currentPage) {
if (currentPage == 1) 0.6f else 0.7f
}
val animatedHeight by animateFloatAsState( val animatedHeight by animateFloatAsState(
targetValue = height, targetValue = if (pagerState.currentPage == 2) 0.6f else 0.7f,
animationSpec = tween(300), animationSpec = tween(300)
label = "Height animation"
) )
LaunchedEffect(currentPage) { LaunchedEffect(pagerState.currentPage) {
onEvent(
ReaderEvent.OnScrollToSettingsPage(
page = pagerState.currentPage,
pagerState = null
)
)
onEvent( onEvent(
ReaderEvent.OnShowHideMenu( ReaderEvent.OnShowHideMenu(
currentPage != 1, pagerState.currentPage != 2,
context as ComponentActivity context as ComponentActivity
) )
) )
@ -91,17 +73,14 @@ fun ReaderSettingsBottomSheet(
onEvent(ReaderEvent.OnShowHideSettingsBottomSheet) onEvent(ReaderEvent.OnShowHideSettingsBottomSheet)
} }
) { ) {
ReaderSettingsBottomSheetTabRow( ReaderSettingsBottomSheetTabRow(pagerState = pagerState)
onEvent = onEvent,
pagerState = pagerState
)
HorizontalPager(state = pagerState) { page -> HorizontalPager(state = pagerState) { page ->
LazyColumn { LazyColumn {
if (page == 0) { if (page == 0) {
if (page == 1) {
LazyColumn {
ReaderSettingsCategory( ReaderSettingsCategory(
state = mainState,
onMainEvent = onMainEvent,
titleColor = { MaterialTheme.colorScheme.onSurface }, titleColor = { MaterialTheme.colorScheme.onSurface },
topPadding = 16.dp, topPadding = 16.dp,
bottomPadding = 8.dp + it bottomPadding = 8.dp + it
@ -109,13 +88,9 @@ fun ReaderSettingsBottomSheet(
} }
} }
LazyColumn { if (page == 2) {
if (page == 1) { LazyColumn {
ColorsSubcategory( ColorsSubcategory(
state = mainState,
settingsState = settingsState,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent,
showTitle = false, showTitle = false,
backgroundColor = { MaterialTheme.colorScheme.surfaceContainer }, backgroundColor = { MaterialTheme.colorScheme.surfaceContainer },
topPadding = 16.dp, topPadding = 16.dp,

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
/** /**
@ -24,6 +25,9 @@ fun ReaderSettingsBottomSheetTabRow(
pagerState: PagerState pagerState: PagerState
) { ) {
val tabItems = listOf( val tabItems = listOf(
fun ReaderSettingsBottomSheetTabRow(pagerState: PagerState) {
val onEvent = LocalReaderViewModel.current.onEvent
stringResource(id = R.string.general_tab), stringResource(id = R.string.general_tab),
stringResource(id = R.string.color_tab) stringResource(id = R.string.color_tab)
) )

View file

@ -23,7 +23,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -33,16 +32,16 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CustomCoverImage import ua.acclorite.book_story.presentation.components.CustomCoverImage
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
/** /**
* Reader start item. Displays at the beginning of the book. * Reader start item. Displays at the beginning of the book.
*
* @param state [ReaderState].
*/ */
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun ReaderStartItem(state: State<ReaderState>) { fun ReaderStartItem() {
val state = LocalReaderViewModel.current.state
val statusBarHeight = WindowInsets val statusBarHeight = WindowInsets
.statusBarsIgnoringVisibility .statusBarsIgnoringVisibility
.asPaddingValues() .asPaddingValues()

View file

@ -36,7 +36,7 @@ sealed class ReaderEvent {
data class OnScroll(val progress: Float) : ReaderEvent() data class OnScroll(val progress: Float) : ReaderEvent()
data object OnShowHideSettingsBottomSheet : ReaderEvent() data object OnShowHideSettingsBottomSheet : ReaderEvent()
data class OnScrollToSettingsPage(val page: Int, val pagerState: PagerState) : ReaderEvent() data class OnScrollToSettingsPage(val page: Int, val pagerState: PagerState?) : ReaderEvent()
data class OnMoveBookToAlreadyRead( data class OnMoveBookToAlreadyRead(
val context: ComponentActivity, val context: ComponentActivity,
val onUpdateCategories: (Book) -> Unit, val onUpdateCategories: (Book) -> Unit,

View file

@ -10,7 +10,6 @@ import androidx.compose.runtime.snapshotFlow
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -32,12 +31,11 @@ import ua.acclorite.book_story.domain.use_case.GetBookById
import ua.acclorite.book_story.domain.use_case.GetLatestHistory import ua.acclorite.book_story.domain.use_case.GetLatestHistory
import ua.acclorite.book_story.domain.use_case.GetText import ua.acclorite.book_story.domain.use_case.GetText
import ua.acclorite.book_story.domain.use_case.UpdateBooks import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.OnNavigate import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.UIText import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.launchActivity import ua.acclorite.book_story.presentation.data.launchActivity
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -48,14 +46,14 @@ class ReaderViewModel @Inject constructor(
private val getText: GetText, private val getText: GetText,
private val getLatestHistory: GetLatestHistory, private val getLatestHistory: GetLatestHistory,
private val getBookById: GetBookById private val getBookById: GetBookById
) : ViewModel() { ) : BaseViewModel<ReaderState, ReaderEvent>() {
private val _state = MutableStateFlow(ReaderState()) private val _state = MutableStateFlow(ReaderState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private var eventJob = SupervisorJob() private var eventJob = SupervisorJob()
fun onEvent(event: ReaderEvent) { override fun onEvent(event: ReaderEvent) {
viewModelScope.launch(eventJob + Dispatchers.Main) { viewModelScope.launch(eventJob + Dispatchers.Main) {
when (event) { when (event) {
is ReaderEvent.OnTextIsEmpty -> { is ReaderEvent.OnTextIsEmpty -> {
@ -269,7 +267,6 @@ class ReaderViewModel @Inject constructor(
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
_state.update { _state.update {
it.copy( it.copy(
currentPage = if (it.showSettingsBottomSheet) it.currentPage else 0,
showSettingsBottomSheet = !it.showSettingsBottomSheet showSettingsBottomSheet = !it.showSettingsBottomSheet
) )
} }
@ -278,7 +275,13 @@ class ReaderViewModel @Inject constructor(
is ReaderEvent.OnScrollToSettingsPage -> { is ReaderEvent.OnScrollToSettingsPage -> {
launch { launch {
event.pagerState.scrollToPage(event.page) _state.update {
event.pagerState?.scrollToPage(event.page)
it.copy(
currentPage = event.page
)
}
} }
} }
@ -498,10 +501,7 @@ class ReaderViewModel @Inject constructor(
} }
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
fun onUpdateProgress( fun onUpdateProgress(refreshList: (Book) -> Unit) {
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit
) {
viewModelScope.launch { viewModelScope.launch {
snapshotFlow { snapshotFlow {
_state.value.listState.firstVisibleItemIndex to _state.value.listState.firstVisibleItemScrollOffset _state.value.listState.firstVisibleItemIndex to _state.value.listState.firstVisibleItemScrollOffset
@ -538,8 +538,7 @@ class ReaderViewModel @Inject constructor(
firstVisibleItemIndex = firstVisibleItemIndex, firstVisibleItemIndex = firstVisibleItemIndex,
firstVisibleItemOffset = firstVisibleItemScrollOffset, firstVisibleItemOffset = firstVisibleItemScrollOffset,
refreshList = { book -> refreshList = { book ->
onLibraryEvent(LibraryEvent.OnUpdateBook(book)) refreshList(book)
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
} }
) )
) )

View file

@ -24,26 +24,22 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.settings.components.SettingsCategoryItem import ua.acclorite.book_story.presentation.screens.settings.components.SettingsCategoryItem
@Composable @Composable
fun SettingsScreenRoot() { fun SettingsScreenRoot() {
val navigator = LocalNavigator.current SettingsScreen()
SettingsScreen(onNavigate = { navigator.it() })
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun SettingsScreen( private fun SettingsScreen() {
onNavigate: OnNavigate val onNavigate = LocalOnNavigate.current
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(

View file

@ -3,7 +3,6 @@ package ua.acclorite.book_story.presentation.screens.settings.data
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.isGranted
@ -24,6 +23,7 @@ import ua.acclorite.book_story.domain.use_case.GetColorPresets
import ua.acclorite.book_story.domain.use_case.ReorderColorPresets import ua.acclorite.book_story.domain.use_case.ReorderColorPresets
import ua.acclorite.book_story.domain.use_case.SelectColorPreset import ua.acclorite.book_story.domain.use_case.SelectColorPreset
import ua.acclorite.book_story.domain.use_case.UpdateColorPreset import ua.acclorite.book_story.domain.use_case.UpdateColorPreset
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.ID import ua.acclorite.book_story.domain.util.ID
import ua.acclorite.book_story.domain.util.UIText import ua.acclorite.book_story.domain.util.UIText
@ -39,10 +39,10 @@ class SettingsViewModel @Inject constructor(
private val deleteColorPreset: DeleteColorPreset, private val deleteColorPreset: DeleteColorPreset,
private val selectColorPreset: SelectColorPreset, private val selectColorPreset: SelectColorPreset,
private val reorderColorPresets: ReorderColorPresets private val reorderColorPresets: ReorderColorPresets
) : ViewModel() { ) : BaseViewModel<SettingsState, SettingsEvent>() {
private val _state = MutableStateFlow(SettingsState()) private val _state = MutableStateFlow(SettingsState())
val state = _state.asStateFlow() override val state = _state.asStateFlow()
private val _isReady = MutableStateFlow(false) private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow() val isReady = _isReady.asStateFlow()
@ -93,7 +93,7 @@ class SettingsViewModel @Inject constructor(
} }
@Suppress("LABEL_NAME_CLASH") @Suppress("LABEL_NAME_CLASH")
fun onEvent(event: SettingsEvent) { override fun onEvent(event: SettingsEvent) {
when (event) { when (event) {
is SettingsEvent.OnGeneralChangeCheckForUpdates -> { is SettingsEvent.OnGeneralChangeCheckForUpdates -> {
if (!event.enable) { if (!event.enable) {

View file

@ -13,53 +13,25 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.AppearanceSettingsCategory import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.AppearanceSettingsCategory
@Composable @Composable
fun AppearanceSettingsRoot() { fun AppearanceSettingsRoot() {
val navigator = LocalNavigator.current AppearanceSettings()
val mainViewModel: MainViewModel = hiltViewModel()
val settingsViewModel: SettingsViewModel = hiltViewModel()
val state = mainViewModel.state.collectAsState()
val settingsState = settingsViewModel.state.collectAsState()
AppearanceSettings(
state = state,
settingsState = settingsState,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent,
onSettingsEvent = settingsViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun AppearanceSettings( private fun AppearanceSettings() {
state: State<MainState>, val onNavigate = LocalOnNavigate.current
settingsState: State<SettingsState>,
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
@ -91,12 +63,7 @@ private fun AppearanceSettings(
.padding(top = paddingValues.calculateTopPadding()), .padding(top = paddingValues.calculateTopPadding()),
state = scrollState.second state = scrollState.second
) { ) {
AppearanceSettingsCategory( AppearanceSettingsCategory()
state = state,
settingsState = settingsState,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent
)
} }
} }
} }

View file

@ -5,14 +5,9 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ColorsSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ColorsSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ThemePreferencesSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ThemePreferencesSubcategory
@ -20,34 +15,21 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.c
* Appearance Settings Category. * Appearance Settings Category.
* Contains all Appearance settings such as Theme. * Contains all Appearance settings such as Theme.
* *
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param onSettingsEvent [SettingsEvent] callback.
* @param titleColor Title's color. * @param titleColor Title's color.
* @param topPadding Top padding to be applied. * @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied. * @param bottomPadding Bottom padding to be applied.
*/ */
fun LazyListScope.AppearanceSettingsCategory( fun LazyListScope.AppearanceSettingsCategory(
state: State<MainState>,
settingsState: State<SettingsState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
topPadding: Dp = 16.dp, topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp bottomPadding: Dp = 48.dp
) { ) {
ThemePreferencesSubcategory( ThemePreferencesSubcategory(
state = state,
onMainEvent = onMainEvent,
titleColor = titleColor, titleColor = titleColor,
topPadding = topPadding, topPadding = topPadding,
bottomPadding = 0.dp bottomPadding = 0.dp
) )
ColorsSubcategory( ColorsSubcategory(
state = state,
settingsState = settingsState,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent,
titleColor = titleColor, titleColor = titleColor,
topPadding = 22.dp, topPadding = 22.dp,
bottomPadding = bottomPadding bottomPadding = bottomPadding

View file

@ -36,7 +36,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
@ -64,10 +63,10 @@ import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.components.customItemsIndexed import ua.acclorite.book_story.presentation.components.customItemsIndexed
import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
import ua.acclorite.book_story.presentation.ui.Transitions import ua.acclorite.book_story.presentation.ui.Transitions
@ -78,13 +77,14 @@ import ua.acclorite.book_story.presentation.ui.Transitions
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun ColorPresetSetting( fun ColorPresetSetting(
state: State<SettingsState>,
onEvent: (SettingsEvent) -> Unit,
backgroundColor: Color, backgroundColor: Color,
verticalPadding: Dp = 8.dp, verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp horizontalPadding: Dp = 18.dp
) { ) {
val state = LocalSettingsViewModel.current.state
val onEvent = LocalSettingsViewModel.current.onEvent
val context = LocalContext.current val context = LocalContext.current
val reorderableListState = rememberReorderableLazyListState( val reorderableListState = rememberReorderableLazyListState(
lazyListState = state.value.colorPresetsListState lazyListState = state.value.colorPresetsListState
) { from, to -> ) { from, to ->

View file

@ -2,12 +2,11 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.ui.DarkTheme import ua.acclorite.book_story.presentation.ui.DarkTheme
@ -16,10 +15,10 @@ import ua.acclorite.book_story.presentation.ui.DarkTheme
* If true, dark theme applied to the app's theme. * If true, dark theme applied to the app's theme.
*/ */
@Composable @Composable
fun DarkThemeSetting( fun DarkThemeSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SegmentedButtonWithTitle( SegmentedButtonWithTitle(
title = stringResource(id = R.string.dark_theme_option), title = stringResource(id = R.string.dark_theme_option),
buttons = DarkTheme.entries.map { buttons = DarkTheme.entries.map {

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
* If true, user can fast change color presets in Reader with swipes. * If true, user can fast change color presets in Reader with swipes.
*/ */
@Composable @Composable
fun FastColorPresetChangeSetting( fun FastColorPresetChangeSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SwitchWithTitle( SwitchWithTitle(
selected = state.value.fastColorPresetChange, selected = state.value.fastColorPresetChange,
title = stringResource(id = R.string.fast_color_preset_change_option), title = stringResource(id = R.string.fast_color_preset_change_option),

View file

@ -2,12 +2,11 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.ui.ExpandingTransition import ua.acclorite.book_story.presentation.ui.ExpandingTransition
import ua.acclorite.book_story.presentation.ui.PureDark import ua.acclorite.book_story.presentation.ui.PureDark
@ -18,10 +17,10 @@ import ua.acclorite.book_story.presentation.ui.isDark
* If true, enables Pure Dark (OLED) theme. * If true, enables Pure Dark (OLED) theme.
*/ */
@Composable @Composable
fun PureDarkSetting( fun PureDarkSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
ExpandingTransition(visible = state.value.darkTheme.isDark()) { ExpandingTransition(visible = state.value.darkTheme.isDark()) {
SegmentedButtonWithTitle( SegmentedButtonWithTitle(
title = stringResource(id = R.string.pure_dark_option), title = stringResource(id = R.string.pure_dark_option),

View file

@ -3,15 +3,14 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.ui.BookStoryTheme import ua.acclorite.book_story.presentation.ui.BookStoryTheme
import ua.acclorite.book_story.presentation.ui.ExpandingTransition import ua.acclorite.book_story.presentation.ui.ExpandingTransition
@ -25,10 +24,10 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
* Lets user change theme contrast levels. * Lets user change theme contrast levels.
*/ */
@Composable @Composable
fun ThemeContrastSetting( fun ThemeContrastSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
val themeContrastTheme = remember { mutableStateOf(state.value.theme) } val themeContrastTheme = remember { mutableStateOf(state.value.theme) }
LaunchedEffect(state.value.theme) { LaunchedEffect(state.value.theme) {

View file

@ -27,7 +27,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -42,9 +41,9 @@ import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.UIText import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.customItemsIndexed import ua.acclorite.book_story.presentation.components.customItemsIndexed
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.ui.Theme import ua.acclorite.book_story.presentation.ui.Theme
import ua.acclorite.book_story.presentation.ui.ThemeContrast import ua.acclorite.book_story.presentation.ui.ThemeContrast
import ua.acclorite.book_story.presentation.ui.animatedColorScheme import ua.acclorite.book_story.presentation.ui.animatedColorScheme
@ -57,11 +56,12 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
*/ */
@Composable @Composable
fun ThemeSetting( fun ThemeSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
verticalPadding: Dp = 8.dp, verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp horizontalPadding: Dp = 18.dp
) { ) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val themes = remember { val themes = remember {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Constants.THEMES if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Constants.THEMES
else Constants.THEMES.dropWhile { it.first == Theme.DYNAMIC } else Constants.THEMES.dropWhile { it.first == Theme.DYNAMIC }

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -17,10 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsState
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.ColorPresetSetting import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.ColorPresetSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.FastColorPresetChangeSetting import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.FastColorPresetChangeSetting
@ -29,10 +24,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.c
* Contains all settings from Colors. * Contains all settings from Colors.
*/ */
fun LazyListScope.ColorsSubcategory( fun LazyListScope.ColorsSubcategory(
state: State<MainState>,
settingsState: State<SettingsState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.colors_appearance_settings) }, title: @Composable () -> String = { stringResource(id = R.string.colors_appearance_settings) },
backgroundColor: @Composable () -> Color = { MaterialTheme.colorScheme.surfaceContainerLow }, backgroundColor: @Composable () -> Color = { MaterialTheme.colorScheme.surfaceContainerLow },
@ -59,18 +50,11 @@ fun LazyListScope.ColorsSubcategory(
} }
item { item {
ColorPresetSetting( ColorPresetSetting(backgroundColor = backgroundColor.invoke())
state = settingsState,
backgroundColor = backgroundColor.invoke(),
onEvent = onSettingsEvent
)
} }
item { item {
FastColorPresetChangeSetting( FastColorPresetChangeSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.DarkThemeSetting import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.DarkThemeSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.PureDarkSetting import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.PureDarkSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.ThemeContrastSetting import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.ThemeContrastSetting
@ -29,8 +26,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.c
* Contains all settings from Theme Preferences. * Contains all settings from Theme Preferences.
*/ */
fun LazyListScope.ThemePreferencesSubcategory( fun LazyListScope.ThemePreferencesSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.theme_appearance_settings) }, title: @Composable () -> String = { stringResource(id = R.string.theme_appearance_settings) },
showTitle: Boolean = true, showTitle: Boolean = true,
@ -56,31 +51,19 @@ fun LazyListScope.ThemePreferencesSubcategory(
} }
item { item {
DarkThemeSetting( DarkThemeSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
ThemeSetting( ThemeSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
ThemeContrastSetting( ThemeContrastSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
PureDarkSetting( PureDarkSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {

View file

@ -13,44 +13,25 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.screens.settings.nested.browse.components.BrowseSettingsCategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.BrowseSettingsCategory
@Composable @Composable
fun BrowseSettingsRoot() { fun BrowseSettingsRoot() {
val navigator = LocalNavigator.current BrowseSettings()
val mainViewModel: MainViewModel = hiltViewModel()
val state = mainViewModel.state.collectAsState()
BrowseSettings(
state = state,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun BrowseSettings( private fun BrowseSettings() {
state: State<MainState>, val onNavigate = LocalOnNavigate.current
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
@ -82,10 +63,7 @@ private fun BrowseSettings(
.padding(top = paddingValues.calculateTopPadding()), .padding(top = paddingValues.calculateTopPadding()),
state = scrollState.second state = scrollState.second
) { ) {
BrowseSettingsCategory( BrowseSettingsCategory()
state = state,
onMainEvent = onMainEvent
)
} }
} }
} }

View file

@ -3,11 +3,8 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.State
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseFilterSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseFilterSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseGeneralSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseGeneralSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseSortSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.subcategories.BrowseSortSubcategory
@ -16,32 +13,22 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
* Browse Settings Category. * Browse Settings Category.
* Contains all Browse settings. * Contains all Browse settings.
* *
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param topPadding Top padding to be applied. * @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied. * @param bottomPadding Bottom padding to be applied.
*/ */
fun LazyListScope.BrowseSettingsCategory( fun LazyListScope.BrowseSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
topPadding: Dp = 16.dp, topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp bottomPadding: Dp = 48.dp
) { ) {
BrowseGeneralSubcategory( BrowseGeneralSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = topPadding, topPadding = topPadding,
bottomPadding = 0.dp bottomPadding = 0.dp
) )
BrowseFilterSubcategory( BrowseFilterSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = 22.dp, topPadding = 22.dp,
bottomPadding = 0.dp bottomPadding = 0.dp
) )
BrowseSortSubcategory( BrowseSortSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = 22.dp, topPadding = 22.dp,
bottomPadding = bottomPadding bottomPadding = bottomPadding
) )

View file

@ -2,12 +2,11 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.browse.comp
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
@ -16,10 +15,10 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
* Lets user choose between all and folders structure for Browse. * Lets user choose between all and folders structure for Browse.
*/ */
@Composable @Composable
fun BrowseFilesStructureSetting( fun BrowseFilesStructureSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SegmentedButtonWithTitle( SegmentedButtonWithTitle(
title = stringResource(id = R.string.browse_files_structure_option), title = stringResource(id = R.string.browse_files_structure_option),
buttons = BrowseFilesStructure.entries.map { buttons = BrowseFilesStructure.entries.map {

View file

@ -13,25 +13,24 @@ import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
/** /**
* Browse Filter setting. * Browse Filter setting.
* Lets user choose which items to filter in Browse. * Lets user choose which items to filter in Browse.
*/ */
fun LazyListScope.BrowseFilterSetting( fun LazyListScope.BrowseFilterSetting() {
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
customItems(Constants.EXTENSIONS, key = { it }) { customItems(Constants.EXTENSIONS, key = { it }) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
FilterItem( FilterItem(
item = it, item = it,
isSelected = state.value.browseIncludedFilterItems.any { item -> isSelected = state.value.browseIncludedFilterItems.any { item ->

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
import ua.acclorite.book_story.presentation.ui.ExpandingTransition import ua.acclorite.book_story.presentation.ui.ExpandingTransition
@ -15,10 +14,10 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
* Lets user specify grid size to be displayed. * Lets user specify grid size to be displayed.
*/ */
@Composable @Composable
fun BrowseGridSizeSetting( fun BrowseGridSizeSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
ExpandingTransition(visible = state.value.browseLayout == BrowseLayout.GRID) { ExpandingTransition(visible = state.value.browseLayout == BrowseLayout.GRID) {
SliderWithTitle( SliderWithTitle(
value = state.value.browseGridSize value = state.value.browseGridSize

View file

@ -2,12 +2,11 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.browse.comp
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@ -16,10 +15,10 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
* Lets user choose between layouts(list, grid). * Lets user choose between layouts(list, grid).
*/ */
@Composable @Composable
fun BrowseLayoutSetting( fun BrowseLayoutSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SegmentedButtonWithTitle( SegmentedButtonWithTitle(
title = stringResource(id = R.string.browse_layout_option), title = stringResource(id = R.string.browse_layout_option),
buttons = BrowseLayout.entries.map { buttons = BrowseLayout.entries.map {

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
import ua.acclorite.book_story.presentation.ui.ExpandingTransition import ua.acclorite.book_story.presentation.ui.ExpandingTransition
@ -15,10 +14,10 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
* If true, shows all favorite directories in root directory and moves them to the top in nested directories. * If true, shows all favorite directories in root directory and moves them to the top in nested directories.
*/ */
@Composable @Composable
fun BrowsePinFavoriteDirectoriesSetting( fun BrowsePinFavoriteDirectoriesSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
ExpandingTransition(visible = state.value.browseFilesStructure == BrowseFilesStructure.DIRECTORIES) { ExpandingTransition(visible = state.value.browseFilesStructure == BrowseFilesStructure.DIRECTORIES) {
SwitchWithTitle( SwitchWithTitle(
selected = state.value.browsePinFavoriteDirectories, selected = state.value.browsePinFavoriteDirectories,

View file

@ -17,7 +17,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -25,20 +24,20 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.customItems import ua.acclorite.book_story.presentation.components.customItems
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseSortOrder import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseSortOrder
/** /**
* Browse Sort Order setting. * Browse Sort Order setting.
* Lets user choose how to sort files in Browse. * Lets user choose how to sort files in Browse.
*/ */
fun LazyListScope.BrowseSortOrderSetting( fun LazyListScope.BrowseSortOrderSetting() {
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
customItems(BrowseSortOrder.entries, key = { it.name }) { customItems(BrowseSortOrder.entries, key = { it.name }) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SortItem( SortItem(
item = it, item = it,
isSelected = state.value.browseSortOrder == it, isSelected = state.value.browseSortOrder == it,

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseFilterSetting import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseFilterSetting
/** /**
@ -26,8 +23,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
* Contains all settings from Filter Browse settings. * Contains all settings from Filter Browse settings.
*/ */
fun LazyListScope.BrowseFilterSubcategory( fun LazyListScope.BrowseFilterSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.filter_browse_settings) }, title: @Composable () -> String = { stringResource(id = R.string.filter_browse_settings) },
showTitle: Boolean = true, showTitle: Boolean = true,
@ -52,10 +47,7 @@ fun LazyListScope.BrowseFilterSubcategory(
} }
} }
BrowseFilterSetting( BrowseFilterSetting()
state = state,
onMainEvent = onMainEvent
)
item { item {
Spacer( Spacer(

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseFilesStructureSetting import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseFilesStructureSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseGridSizeSetting import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseGridSizeSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseLayoutSetting import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseLayoutSetting
@ -29,8 +26,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
* Contains all settings from General Browse settings. * Contains all settings from General Browse settings.
*/ */
fun LazyListScope.BrowseGeneralSubcategory( fun LazyListScope.BrowseGeneralSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.general_browse_settings) }, title: @Composable () -> String = { stringResource(id = R.string.general_browse_settings) },
showTitle: Boolean = true, showTitle: Boolean = true,
@ -56,31 +51,19 @@ fun LazyListScope.BrowseGeneralSubcategory(
} }
item { item {
BrowseFilesStructureSetting( BrowseFilesStructureSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
BrowsePinFavoriteDirectoriesSetting( BrowsePinFavoriteDirectoriesSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
BrowseLayoutSetting( BrowseLayoutSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
BrowseGridSizeSetting( BrowseGridSizeSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseSortOrderSetting import ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings.BrowseSortOrderSetting
/** /**
@ -26,8 +23,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
* Contains all settings from Sort Browse settings. * Contains all settings from Sort Browse settings.
*/ */
fun LazyListScope.BrowseSortSubcategory( fun LazyListScope.BrowseSortSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.sort_browse_settings) }, title: @Composable () -> String = { stringResource(id = R.string.sort_browse_settings) },
showTitle: Boolean = true, showTitle: Boolean = true,
@ -52,10 +47,7 @@ fun LazyListScope.BrowseSortSubcategory(
} }
} }
BrowseSortOrderSetting( BrowseSortOrderSetting()
state = state,
onMainEvent = onMainEvent
)
item { item {
Spacer( Spacer(

View file

@ -12,50 +12,26 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.GeneralSettingsCategory import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.GeneralSettingsCategory
@Composable @Composable
fun GeneralSettingsRoot() { fun GeneralSettingsRoot() {
val navigator = LocalNavigator.current GeneralSettings()
val settingsViewModel: SettingsViewModel = hiltViewModel()
val mainViewModel: MainViewModel = hiltViewModel()
val state = mainViewModel.state.collectAsState()
GeneralSettings(
state = state,
onNavigate = { navigator.it() },
onSettingsEvent = settingsViewModel::onEvent,
onMainEvent = mainViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun GeneralSettings( private fun GeneralSettings() {
state: State<MainState>, val onNavigate = LocalOnNavigate.current
onNavigate: OnNavigate,
onSettingsEvent: (SettingsEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
@ -86,11 +62,7 @@ private fun GeneralSettings(
.padding(top = paddingValues.calculateTopPadding()), .padding(top = paddingValues.calculateTopPadding()),
state = scrollState.second state = scrollState.second
) { ) {
GeneralSettingsCategory( GeneralSettingsCategory()
state = state,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent
)
} }
} }
} }

View file

@ -5,14 +5,10 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.general.com
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.AppLanguageSetting import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.AppLanguageSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.CheckForUpdatesSetting import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.CheckForUpdatesSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.DoublePressExitSetting import ua.acclorite.book_story.presentation.screens.settings.nested.general.components.settings.DoublePressExitSetting
@ -21,16 +17,10 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.general.comp
* General Settings Category. * General Settings Category.
* Contains all General settings such as App Language. * Contains all General settings such as App Language.
* *
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param onSettingsEvent [SettingsEvent] callback.
* @param topPadding Top padding to be applied. * @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied. * @param bottomPadding Bottom padding to be applied.
*/ */
fun LazyListScope.GeneralSettingsCategory( fun LazyListScope.GeneralSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
topPadding: Dp = 16.dp, topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp bottomPadding: Dp = 48.dp
) { ) {
@ -41,25 +31,15 @@ fun LazyListScope.GeneralSettingsCategory(
} }
item { item {
AppLanguageSetting( AppLanguageSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {
CheckForUpdatesSetting( CheckForUpdatesSetting()
state = state,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent
)
} }
item { item {
DoublePressExitSetting( DoublePressExitSetting()
state = state,
onMainEvent = onMainEvent
)
} }
item { item {

View file

@ -2,13 +2,12 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.general.com
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
/** /**
@ -16,10 +15,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
* Changes app's language. * Changes app's language.
*/ */
@Composable @Composable
fun AppLanguageSetting( fun AppLanguageSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
ChipsWithTitle( ChipsWithTitle(
title = stringResource(id = R.string.language_option), title = stringResource(id = R.string.language_option),
chips = Constants.LANGUAGES.sortedBy { it.second }.map { chips = Constants.LANGUAGES.sortedBy { it.second }.map {

View file

@ -6,7 +6,6 @@ import androidx.activity.ComponentActivity
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Security import androidx.compose.material.icons.filled.Security
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -14,9 +13,10 @@ import androidx.compose.ui.res.stringResource
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent import ua.acclorite.book_story.presentation.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
@ -27,12 +27,12 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
@Composable @Composable
fun CheckForUpdatesSetting( fun CheckForUpdatesSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit, val onMainEvent = LocalMainViewModel.current.onEvent
onSettingsEvent: (SettingsEvent) -> Unit val onSettingsEvent = LocalSettingsViewModel.current.onEvent
) {
val activity = LocalContext.current as ComponentActivity val activity = LocalContext.current as ComponentActivity
val notificationsPermissionState = rememberPermissionState( val notificationsPermissionState = rememberPermissionState(
permission = Manifest.permission.POST_NOTIFICATIONS permission = Manifest.permission.POST_NOTIFICATIONS
) )

View file

@ -2,11 +2,10 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.general.com
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/** /**
@ -15,10 +14,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/ */
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
@Composable @Composable
fun DoublePressExitSetting( fun DoublePressExitSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SwitchWithTitle( SwitchWithTitle(
selected = state.value.doublePressExit, selected = state.value.doublePressExit,
title = stringResource(id = R.string.double_press_exit_option), title = stringResource(id = R.string.double_press_exit_option),

View file

@ -12,44 +12,25 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.screens.settings.nested.reader.components.ReaderSettingsCategory import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.ReaderSettingsCategory
@Composable @Composable
fun ReaderSettingsRoot() { fun ReaderSettingsRoot() {
val navigator = LocalNavigator.current ReaderSettings()
val mainViewModel: MainViewModel = hiltViewModel()
val state = mainViewModel.state.collectAsState()
ReaderSettings(
state = state,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent
)
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun ReaderSettings( private fun ReaderSettings() {
state: State<MainState>, val onNavigate = LocalOnNavigate.current
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit
) {
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState() val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold( Scaffold(
@ -80,10 +61,7 @@ private fun ReaderSettings(
.padding(top = paddingValues.calculateTopPadding()), .padding(top = paddingValues.calculateTopPadding()),
state = scrollState.second state = scrollState.second
) { ) {
ReaderSettingsCategory( ReaderSettingsCategory()
state = state,
onMainEvent = onMainEvent
)
} }
} }
} }

View file

@ -5,13 +5,10 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.CategoryTitle import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.FontSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.FontSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.TextSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.TextSubcategory
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.TranslatorSubcategory import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.subcategories.TranslatorSubcategory
@ -20,36 +17,26 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.reader.compo
* Reader Settings Category. * Reader Settings Category.
* Contains all Reader settings. * Contains all Reader settings.
* *
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param titleColor Color that [CategoryTitle] has. * @param titleColor Color that [CategoryTitle] has.
* @param topPadding Top padding to be applied. * @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied. * @param bottomPadding Bottom padding to be applied.
*/ */
fun LazyListScope.ReaderSettingsCategory( fun LazyListScope.ReaderSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary }, titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
topPadding: Dp = 16.dp, topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp bottomPadding: Dp = 48.dp
) { ) {
FontSubcategory( FontSubcategory(
state = state,
onMainEvent = onMainEvent,
titleColor = titleColor, titleColor = titleColor,
topPadding = topPadding, topPadding = topPadding,
bottomPadding = 0.dp bottomPadding = 0.dp
) )
TextSubcategory( TextSubcategory(
state,
onMainEvent = onMainEvent,
titleColor = titleColor, titleColor = titleColor,
topPadding = 22.dp, topPadding = 22.dp,
bottomPadding = 0.dp bottomPadding = 0.dp
) )
TranslatorSubcategory( TranslatorSubcategory(
state,
onMainEvent = onMainEvent,
titleColor = titleColor, titleColor = titleColor,
topPadding = 22.dp, topPadding = 22.dp,
bottomPadding = bottomPadding bottomPadding = bottomPadding

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
* Changes Reader's double click translation. * Changes Reader's double click translation.
*/ */
@Composable @Composable
fun DoubleClickTranslationSetting( fun DoubleClickTranslationSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SwitchWithTitle( SwitchWithTitle(
selected = state.value.doubleClickTranslation, selected = state.value.doubleClickTranslation,
title = stringResource(id = R.string.double_click_translation_option), title = stringResource(id = R.string.double_click_translation_option),

View file

@ -2,14 +2,13 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
/** /**
@ -17,10 +16,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
* Changes Reader's font, uses fonts from [Constants.FONTS]. * Changes Reader's font, uses fonts from [Constants.FONTS].
*/ */
@Composable @Composable
fun FontFamilySetting( fun FontFamilySetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
val fontFamily = remember(state.value.fontFamily) { val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find { Constants.FONTS.find {
it.id == state.value.fontFamily it.id == state.value.fontFamily

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
* Changes Reader's font size. * Changes Reader's font size.
*/ */
@Composable @Composable
fun FontSizeSetting( fun FontSizeSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SliderWithTitle( SliderWithTitle(
value = state.value.fontSize to "pt", value = state.value.fontSize to "pt",
fromValue = 10, fromValue = 10,

View file

@ -2,15 +2,14 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
/** /**
@ -18,10 +17,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.Segmente
* Changes Reader's font style (Normal/Italic). * Changes Reader's font style (Normal/Italic).
*/ */
@Composable @Composable
fun FontStyleSetting( fun FontStyleSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
val fontFamily = remember(state.value.fontFamily) { val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find { Constants.FONTS.find {
it.id == state.value.fontFamily it.id == state.value.fontFamily

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
* Changes Reader's letter spacing. * Changes Reader's letter spacing.
*/ */
@Composable @Composable
fun LetterSpacingSetting( fun LetterSpacingSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SliderWithTitle( SliderWithTitle(
value = state.value.letterSpacing to "pt", value = state.value.letterSpacing to "pt",
fromValue = -8, fromValue = -8,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
* Changes Reader's line height. * Changes Reader's line height.
*/ */
@Composable @Composable
fun LineHeightSetting( fun LineHeightSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SliderWithTitle( SliderWithTitle(
value = state.value.lineHeight to "pt", value = state.value.lineHeight to "pt",
fromValue = 1, fromValue = 1,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
* Changes Reader's paragraph height. * Changes Reader's paragraph height.
*/ */
@Composable @Composable
fun ParagraphHeightSetting( fun ParagraphHeightSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SliderWithTitle( SliderWithTitle(
value = state.value.paragraphHeight to "pt", value = state.value.paragraphHeight to "pt",
fromValue = 0, fromValue = 0,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/** /**
@ -13,10 +12,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
* Changes Reader's paragraph indentation. * Changes Reader's paragraph indentation.
*/ */
@Composable @Composable
fun ParagraphIndentationSetting( fun ParagraphIndentationSetting() {
state: State<MainState>, val state = LocalMainViewModel.current.state
onMainEvent: (MainEvent) -> Unit val onMainEvent = LocalMainViewModel.current.onEvent
) {
SwitchWithTitle( SwitchWithTitle(
selected = state.value.paragraphIndentation, selected = state.value.paragraphIndentation,
title = stringResource(id = R.string.paragraph_indentation_option) title = stringResource(id = R.string.paragraph_indentation_option)

Some files were not shown because too many files have changed in this diff Show more