🛠️ 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.view.WindowCompat
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.custom_navigation_rail.CustomNavigationRail
import ua.acclorite.book_story.presentation.data.MainViewModel
@ -89,8 +90,8 @@ class Activity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val state = LocalMainViewModel.current.state
val isLoaded = mainViewModel.isReady.collectAsState()
val state = mainViewModel.state.collectAsState()
val density = LocalDensity.current
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() },
) : Parcelable {
companion object {
private fun <D> provideDefaultValue(calculation: () -> D): D {
return calculation()
}
/**
* Initializes [MainState] by given [Map].
* If no value provided in [data], assigns default value.
@ -210,7 +214,3 @@ 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
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
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.GetAllSettings
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.DataStoreConstants
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
@ -41,7 +41,7 @@ class MainViewModel @Inject constructor(
private val changeLanguage: ChangeLanguage,
private val checkForUpdates: CheckForUpdates,
private val getAllSettings: GetAllSettings
) : ViewModel() {
) : BaseViewModel<MainState, MainEvent>() {
private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow()
@ -52,9 +52,9 @@ class MainViewModel @Inject constructor(
private val _state: MutableStateFlow<MainState> = MutableStateFlow(
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) {
is MainEvent.OnChangeLanguage -> {
viewModelScope.launch(Dispatchers.Main) {

View file

@ -44,6 +44,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
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.presentation.components.CustomAnimatedVisibility
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"
/**
* Passed in [CompositionLocalProvider] and can be accessed through [LocalNavigator].current.
* Passed in [CompositionLocalProvider].
*/
val LocalNavigator = compositionLocalOf<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].
@ -434,7 +438,10 @@ fun NavigationHost(
.background(colorBetweenAnimations)
)
CompositionLocalProvider(LocalNavigator provides navigator) {
CompositionLocalProvider(
LocalNavigator provides navigator,
LocalOnNavigate provides { navigator.it() }
) {
content(navigator)
}
}

View file

@ -19,8 +19,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn
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.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.showToast
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.badges.AboutBadges
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
fun AboutScreenRoot() {
val navigator = LocalNavigator.current
val aboutViewModel: AboutViewModel = hiltViewModel()
val state = aboutViewModel.state.collectAsState()
AboutScreen(
state = state,
onNavigate = { navigator.it() },
onEvent = aboutViewModel::onEvent
)
AboutScreen()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AboutScreen(
state: State<AboutState>,
onNavigate: OnNavigate,
onEvent: (AboutEvent) -> Unit
) {
private fun AboutScreen() {
val state = LocalAboutViewModel.current.state
val onEvent = LocalAboutViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
if (state.value.showUpdateDialog) {
AboutUpdateDialog(
state = state,
onEvent = onEvent
)
AboutUpdateDialog()
}
Scaffold(
@ -245,7 +228,7 @@ private fun AboutScreen(
}
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.filled.Update
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.data.showToast
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.
*/
@Composable
fun AboutUpdateDialog(
state: State<AboutState>,
onEvent: (AboutEvent) -> Unit
) {
fun AboutUpdateDialog() {
val state = LocalAboutViewModel.current.state
val onEvent = LocalAboutViewModel.current.onEvent
val context = LocalContext.current
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 ua.acclorite.book_story.R
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.data.showToast
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.
* Links all ways to contact me.
*
* @param onEvent [AboutEvent] callback.
* @param verticalPadding Vertical item padding.
*/
@Composable
fun AboutBadges(
onEvent: (AboutEvent) -> Unit,
verticalPadding: Dp = 18.dp
) {
fun AboutBadges(verticalPadding: Dp = 18.dp) {
val onEvent = LocalAboutViewModel.current.onEvent
val context = LocalContext.current
Box(

View file

@ -3,7 +3,6 @@ package ua.acclorite.book_story.presentation.screens.about.data
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@ -12,18 +11,19 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.R
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 javax.inject.Inject
@HiltViewModel
class AboutViewModel @Inject constructor(
private val checkForUpdates: CheckForUpdates,
) : ViewModel() {
) : BaseViewModel<AboutState, AboutEvent>() {
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) {
is AboutEvent.OnNavigateToBrowserPage -> {
viewModelScope.launch {

View file

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

View file

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

View file

@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withContext
@ -14,18 +13,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
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.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.launchActivity
import javax.inject.Inject
@HiltViewModel
class LicenseInfoViewModel @Inject constructor() : ViewModel() {
class LicenseInfoViewModel @Inject constructor(
) : BaseViewModel<LicenseInfoState, LicenseInfoEvent>() {
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) {
is LicenseInfoEvent.OnOpenLicensePage -> {
viewModelScope.launch {

View file

@ -18,51 +18,40 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomLazyColumn
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.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.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
fun LicensesScreenRoot() {
val navigator = LocalNavigator.current
val viewModel = LocalLicensesViewModel.current.viewModel
val context = LocalContext.current
val licensesViewModel: LicensesViewModel = hiltViewModel()
val state = licensesViewModel.state.collectAsState()
LaunchedEffect(Unit) {
licensesViewModel.init(context)
viewModel.init(context)
}
LicensesScreen(
state = state,
onNavigate = { navigator.it() }
)
LicensesScreen()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LicensesScreen(
state: State<LicensesState>,
onNavigate: OnNavigate
) {
private fun LicensesScreen() {
val state = LocalLicensesViewModel.current.state
val onNavigate = LocalOnNavigate.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState(
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
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withJson
@ -12,15 +11,18 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.BaseViewModel
import javax.inject.Inject
@HiltViewModel
class LicensesViewModel @Inject constructor(
) : ViewModel() {
) : BaseViewModel<LicensesState, LicensesEvent>() {
private val _state = MutableStateFlow(LicensesState())
val state = _state.asStateFlow()
override val state = _state.asStateFlow()
override fun onEvent(event: LicensesEvent) {}
fun init(context: Context) {
viewModelScope.launch(Dispatchers.IO) {

View file

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

View file

@ -13,7 +13,6 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.unit.dp
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.BookInfoState
/**
* Description section.
*
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BookInfoDescriptionSection(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit
) {
fun BookInfoDescriptionSection() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val descriptionFocusRequester = remember { FocusRequester() }
if (!state.value.editDescription) {

View file

@ -25,7 +25,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -40,21 +39,18 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.BookInfoState
/**
* BookInfo's Info section.
*
* @param state [BookInfoState] instance.
* @param onEvent [BookInfoEvent] callback.
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BookInfoInfoSection(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit
) {
fun BookInfoInfoSection() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val titleFocusRequester = 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.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
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.presentation.components.CustomDropDownMenuItem
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.BookInfoState
/**
* Book info more dropdown. Has three options: Delete, Move and Details.
*/
@Composable
fun BookInfoMoreDropDown(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit,
snackbarState: SnackbarHostState
) {
fun BookInfoMoreDropDown(snackbarState: SnackbarHostState) {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
var showDropDown by remember { mutableStateOf(false) }
Box {

View file

@ -9,7 +9,6 @@ import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
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.unit.dp
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.removeTrailingZero
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
/**
* Statistic section.
*/
@Composable
fun BookInfoStatisticSection(state: State<BookInfoState>) {
fun BookInfoStatisticSection() {
val state = LocalBookInfoViewModel.current.state
val progress = remember(state.value.book) {
"${
(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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.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.BookInfoState
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.ui.DefaultTransition
@ -35,26 +36,22 @@ import ua.acclorite.book_story.presentation.ui.Transitions
/**
* 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 snackbarState [SnackbarHostState].
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BookInfoTopBar(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit,
onNavigate: OnNavigate,
listState: LazyListState,
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 firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
AnimatedTopAppBar(
@ -125,11 +122,7 @@ fun BookInfoTopBar(
!state.value.editAuthor &&
!state.value.editDescription
) {
BookInfoMoreDropDown(
state = state,
onEvent = onEvent,
snackbarState = snackbarState
)
BookInfoMoreDropDown(snackbarState = snackbarState)
}
CustomAnimatedVisibility(
visible = state.value.editTitle ||
@ -175,7 +168,8 @@ fun BookInfoTopBar(
onLibraryEvent(LibraryEvent.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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.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.library.data.LibraryEvent
/**
* 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
fun BookInfoChangeCoverBottomSheet(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit,
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
fun BookInfoChangeCoverBottomSheet() {
val state = LocalBookInfoViewModel.current.state
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onEvent = LocalBookInfoViewModel.current.onEvent
val context = LocalContext.current
val photoPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
@ -51,8 +47,8 @@ fun BookInfoChangeCoverBottomSheet(
uri,
context,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}
)
)
@ -83,8 +79,8 @@ fun BookInfoChangeCoverBottomSheet(
onEvent(
BookInfoEvent.OnResetCoverImage(
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
showResult = {
it.asString(context).showToast(context = context)
@ -117,8 +113,8 @@ fun BookInfoChangeCoverBottomSheet(
onEvent(
BookInfoEvent.OnDeleteCover(
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.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.res.stringResource
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.screens.book_info.data.BookInfoEvent
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.
*
* @param snackbarHostState [SnackbarHostState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/
@Composable
fun BookInfoConfirmUpdateDialog(
snackbarHostState: SnackbarHostState,
onEvent: (BookInfoEvent) -> Unit,
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit,
) {
fun BookInfoConfirmUpdateDialog(snackbarHostState: SnackbarHostState) {
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val context = LocalContext.current
CustomDialogWithContent(
@ -45,8 +43,8 @@ fun BookInfoConfirmUpdateDialog(
snackbarState = snackbarHostState,
context = context,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}
)
)
@ -54,11 +52,3 @@ fun BookInfoConfirmUpdateDialog(
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.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -12,9 +11,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import java.io.File
import java.text.SimpleDateFormat
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.
*
* @param state [BookInfoState] instance.
* @param onEvent [BookInfoEvent] callback.
*/
@Composable
fun BookInfoDetailsBottomSheet(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit
) {
fun BookInfoDetailsBottomSheet() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val context = LocalContext.current
val pattern = remember {

View file

@ -6,8 +6,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.browse.data.BrowseEvent
@ -16,20 +20,14 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/**
* Delete dialog. Deletes current book.
*
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryLoadEvent [LibraryEvent] callback.
* @param onHistoryLoadEvent [HistoryEvent] callback.
* @param onBrowseLoadEvent [BrowseEvent] callback.
*/
@Composable
fun BookInfoDeleteDialog(
onEvent: (BookInfoEvent) -> Unit,
onLibraryLoadEvent: (LibraryEvent.OnLoadList) -> Unit,
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit,
onBrowseLoadEvent: (BrowseEvent.OnLoadList) -> Unit
) {
val navigator = LocalNavigator.current
fun BookInfoDeleteDialog() {
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onBrowseEvent = LocalBrowseViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current
CustomDialogWithContent(
@ -45,11 +43,11 @@ fun BookInfoDeleteDialog(
onAction = {
onEvent(
BookInfoEvent.OnDeleteBook(
onNavigate = { navigator.it() },
onNavigate = onNavigate,
refreshList = {
onLibraryLoadEvent(LibraryEvent.OnLoadList)
onBrowseLoadEvent(BrowseEvent.OnLoadList)
onHistoryLoadEvent(HistoryEvent.OnLoadList)
onLibraryEvent(LibraryEvent.OnLoadList)
onBrowseEvent(BrowseEvent.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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.custom_dialog.CustomDialogWithLazyColumn
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.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.library.data.LibraryEvent
/**
* Move dialog.
* Moves current book to the selected category.
*
* @param state [BookInfoState].
* @param onEvent [BookInfoEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/
@Composable
fun BookInfoMoveDialog(
state: State<BookInfoState>,
onEvent: (BookInfoEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
val navigator = LocalNavigator.current
fun BookInfoMoveDialog() {
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 categories = remember {
@ -61,12 +56,12 @@ fun BookInfoMoveDialog(
BookInfoEvent.OnMoveBook(
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
updatePage = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(it))
},
onNavigate = { navigator.it() }
onNavigate = onNavigate
)
)
context.getString(R.string.book_moved)

View file

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

View file

@ -14,24 +14,19 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.rememberPermissionState
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.data.showToast
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.top_bar.BrowseTopBar
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.ui.DefaultTransition
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun BrowseScreenRoot() {
val navigator = LocalNavigator.current
val viewModel: BrowseViewModel = hiltViewModel()
val libraryViewModel: LibraryViewModel = hiltViewModel()
val mainViewModel: MainViewModel = hiltViewModel()
val state = viewModel.state.collectAsState()
val mainState = mainViewModel.state.collectAsState()
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val viewModel = LocalBrowseViewModel.current.viewModel
val permissionState = rememberPermissionState(
permission = Manifest.permission.READ_EXTERNAL_STORAGE
@ -72,22 +60,14 @@ fun BrowseScreenRoot() {
}
LaunchedEffect(Unit) {
viewModel.onEvent(BrowseEvent.OnUpdateScrollOffset)
viewModel.onEvent(
BrowseEvent.OnPermissionCheck(permissionState)
)
onEvent(BrowseEvent.OnUpdateScrollOffset)
onEvent(BrowseEvent.OnPermissionCheck(permissionState))
}
BrowseScreen(
state = state,
mainState = mainState,
permissionState = permissionState,
filteredFiles = filteredFiles.value,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onMainEvent = mainViewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
)
DisposableEffect(Unit) {
@ -103,16 +83,15 @@ fun BrowseScreenRoot() {
)
@Composable
private fun BrowseScreen(
state: State<BrowseState>,
mainState: State<MainState>,
permissionState: PermissionState,
filteredFiles: List<SelectableFile>,
onNavigate: OnNavigate,
onEvent: (BrowseEvent) -> Unit,
onMainEvent: (MainEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
filteredFiles: List<SelectableFile>
) {
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val context = LocalContext.current
val refreshState = rememberPullRefreshState(
refreshing = state.value.isRefreshing,
onRefresh = {
@ -121,22 +100,13 @@ private fun BrowseScreen(
)
if (state.value.requestPermissionDialog) {
BrowseStoragePermissionDialog(onEvent, permissionState)
BrowseStoragePermissionDialog(permissionState)
}
if (state.value.showAddingDialog) {
BrowseAddingDialog(
state = state,
onEvent = onEvent,
onLibraryEvent = onLibraryEvent
)
BrowseAddingDialog()
}
if (state.value.showFilterBottomSheet) {
BrowseFilterBottomSheet(
state = state,
mainState = mainState,
onMainEvent = onMainEvent,
onEvent = onEvent
)
BrowseFilterBottomSheet()
}
Scaffold(
@ -145,12 +115,7 @@ private fun BrowseScreen(
.pullRefresh(refreshState),
containerColor = MaterialTheme.colorScheme.surface,
topBar = {
BrowseTopBar(
state = state,
mainState = mainState,
filteredFiles = filteredFiles,
onEvent = onEvent
)
BrowseTopBar(filteredFiles = filteredFiles)
}
) { padding ->
Box(
@ -160,8 +125,6 @@ private fun BrowseScreen(
) {
DefaultTransition(visible = !state.value.isLoading) {
BrowseLayout(
state = state,
mainState = mainState,
filteredFiles = filteredFiles,
onLongItemClick = { selectableFile ->
when (selectableFile.isDirectory) {
@ -223,11 +186,8 @@ private fun BrowseScreen(
}
BrowseEmptyPlaceholder(
state = state,
isFilesEmpty = filteredFiles.isEmpty(),
storagePermissionState = permissionState,
onNavigate = onNavigate,
onEvent = onEvent
)
PullRefreshIndicator(
@ -255,9 +215,7 @@ private fun BrowseScreen(
state.value.inNestedDirectory
&& mainState.value.browseFilesStructure != BrowseFilesStructure.ALL_FILES
) {
onEvent(
BrowseEvent.OnGoBackDirectory
)
onEvent(BrowseEvent.OnGoBackDirectory)
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.PermissionState
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.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.components.is_messages.IsError
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
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.BrowseState
import ua.acclorite.book_story.presentation.ui.Transitions
/**
* Browse Empty Placeholder.
* Shows error or empty message.
*
* @param state [BrowseState].
* @param isFilesEmpty Whether the list is empty.
* @param storagePermissionState Storage [PermissionState].
* @param onNavigate Navigator callback.
* @param onEvent [BrowseEvent] callback.
*/
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun BoxScope.BrowseEmptyPlaceholder(
state: State<BrowseState>,
isFilesEmpty: Boolean,
storagePermissionState: PermissionState,
onNavigate: OnNavigate,
onEvent: (BrowseEvent) -> Unit
storagePermissionState: PermissionState
) {
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
CustomAnimatedVisibility(
visible = state.value.isError,
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.PermissionState
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.screens.browse.data.BrowseEvent
/**
* Storage permission dialog.
*
* @param onEvent [BrowseEvent] callback.
* @param permissionState Storage [PermissionState].
*/
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun BrowseStoragePermissionDialog(
onEvent: (BrowseEvent) -> Unit,
permissionState: PermissionState
) {
val activity = LocalContext.current as ComponentActivity
val onEvent = LocalBrowseViewModel.current.onEvent
CustomDialogWithContent(
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.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap
@ -18,30 +17,26 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.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.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
/**
* Adding dialog.
* Adds all selected books to the Library.
*
* @param state [BrowseState].
* @param onEvent [BrowseEvent] callback.
* @param onLibraryEvent [LibraryEvent] callback.
*/
@Composable
fun BrowseAddingDialog(
state: State<BrowseState>,
onEvent: (BrowseEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
fun BrowseAddingDialog() {
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(
title = stringResource(id = R.string.add_books),
@ -54,7 +49,7 @@ fun BrowseAddingDialog(
onAction = {
onEvent(
BrowseEvent.OnAddBooks(
onNavigate = { navigator.it() },
onNavigate = onNavigate,
resetScroll = {
onLibraryEvent(LibraryEvent.OnUpdateCurrentPage(0))
onLibraryEvent(LibraryEvent.OnLoadList)

View file

@ -8,14 +8,11 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.CustomBottomSheet
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.components.LocalBrowseViewModel
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.BrowseGeneralSubcategory
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.
* 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
fun BrowseFilterBottomSheet(
state: State<BrowseState>,
mainState: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
onEvent: (BrowseEvent) -> Unit
) {
fun BrowseFilterBottomSheet() {
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val pagerState = rememberPagerState(state.value.currentPage) { 3 }
LaunchedEffect(pagerState.currentPage) {
@ -67,8 +57,6 @@ fun BrowseFilterBottomSheet(
0 -> {
LazyColumn(Modifier.fillMaxSize()) {
BrowseGeneralSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false,
topPadding = 16.dp,
bottomPadding = 8.dp + it
@ -79,8 +67,6 @@ fun BrowseFilterBottomSheet(
1 -> {
LazyColumn(Modifier.fillMaxSize()) {
BrowseFilterSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false,
topPadding = 16.dp,
bottomPadding = 8.dp + it
@ -91,8 +77,6 @@ fun BrowseFilterBottomSheet(
2 -> {
LazyColumn(Modifier.fillMaxSize()) {
BrowseSortSubcategory(
state = mainState,
onMainEvent = onMainEvent,
showTitle = false,
topPadding = 16.dp,
bottomPadding = 8.dp + it

View file

@ -1,20 +1,16 @@
package ua.acclorite.book_story.presentation.screens.browse.components.layout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
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.list.BrowseListLayout
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
/**
* Browse Layout.
* Shows Grid or List.
*
* @param state [BrowseState].
* @param mainState [MainState].
* @param filteredFiles Filtered items.
* @param onLongItemClick OnLongItemClick callback.
* @param onFavoriteItemClick OnFavoriteItemClick callback.
@ -22,17 +18,16 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
*/
@Composable
fun BrowseLayout(
state: State<BrowseState>,
mainState: State<MainState>,
filteredFiles: List<SelectableFile>,
onLongItemClick: (SelectableFile) -> Unit,
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit
) {
val mainState = LocalMainViewModel.current.state
when (mainState.value.browseLayout) {
BrowseLayout.LIST -> {
BrowseListLayout(
state = state,
filteredFiles = filteredFiles,
onLongItemClick = onLongItemClick,
onFavoriteItemClick = onFavoriteItemClick,
@ -42,7 +37,6 @@ fun BrowseLayout(
BrowseLayout.GRID -> {
BrowseGridLayout(
state = state,
gridSize = mainState.value.browseGridSize,
autoGridSize = mainState.value.browseAutoGridSize,
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.LazyVerticalGrid
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
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.header
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
@Composable
fun BrowseGridLayout(
state: State<BrowseState>,
gridSize: Int,
autoGridSize: Boolean,
filteredFiles: List<SelectableFile>,
@ -27,6 +25,8 @@ fun BrowseGridLayout(
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit,
) {
val state = LocalBrowseViewModel.current.state
LazyVerticalGrid(
columns = if (autoGridSize) GridCells.Adaptive(170.dp)
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.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
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.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
@Composable
fun BrowseListLayout(
state: State<BrowseState>,
filteredFiles: List<SelectableFile>,
onLongItemClick: (SelectableFile) -> Unit,
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit,
) {
val state = LocalBrowseViewModel.current.state
LazyColumn(
state = state.value.listState,
modifier = Modifier

View file

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

View file

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

View file

@ -9,7 +9,6 @@ import android.os.Environment
import android.provider.Settings
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi
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.InsertBook
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.Screen
import ua.acclorite.book_story.presentation.data.launchActivity
@ -43,10 +43,10 @@ class BrowseViewModel @Inject constructor(
private val getFilesFromDevice: GetFilesFromDevice,
private val insertBook: InsertBook,
private val updateFavoriteDirectory: UpdateFavoriteDirectory,
) : ViewModel() {
) : BaseViewModel<BrowseState, BrowseEvent>() {
private val _state = MutableStateFlow(BrowseState())
val state = _state.asStateFlow()
override val state = _state.asStateFlow()
private var searchQueryJob: 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) {
is BrowseEvent.OnStoragePermissionRequest -> {
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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomLazyColumn
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.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.MainViewModel
import ua.acclorite.book_story.presentation.data.Screen
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
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.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.StartViewModel
@Composable
fun HelpScreenRoot(screen: Screen.Help) {
val navigator = LocalNavigator.current
val helpViewModel: HelpViewModel = hiltViewModel()
val browseViewModel: BrowseViewModel = hiltViewModel()
val mainViewModel: MainViewModel = hiltViewModel()
val startViewModel: StartViewModel = hiltViewModel()
val state = helpViewModel.state.collectAsState()
val viewModel = LocalHelpViewModel.current.viewModel
LaunchedEffect(Unit) {
helpViewModel.init(screen = screen)
viewModel.init(screen = screen)
}
HelpScreen(
state = state,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent,
onBrowseEvent = browseViewModel::onEvent,
onStartEvent = startViewModel::onEvent
)
HelpScreen()
}
@OptIn(
ExperimentalMaterial3Api::class
)
@Composable
private fun HelpScreen(
state: State<HelpState>,
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit,
onBrowseEvent: (BrowseEvent) -> Unit,
onStartEvent: (StartEvent) -> Unit
) {
private fun HelpScreen() {
val state = LocalHelpViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val onBrowseEvent = LocalBrowseViewModel.current.onEvent
val onStartEvent = LocalStartViewModel.current.onEvent
val onNavigate = LocalOnNavigate.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold(
@ -171,7 +154,6 @@ private fun HelpScreen(
customItems(Constants.HELP_TIPS, key = { it.title }) { helpTip ->
HelpItem(
helpTip = helpTip,
onNavigate = onNavigate,
fromStart = state.value.fromStart
)
}

View file

@ -31,7 +31,7 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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
/**
@ -39,15 +39,15 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
* Can be Expanded or Collapsed.
*
* @param helpTip [HelpTip] to be shown.
* @param onNavigate Navigator callback.
* @param fromStart Whether user came from Start screen.
*/
@Composable
fun HelpItem(
helpTip: HelpTip,
onNavigate: OnNavigate,
fromStart: Boolean
) {
val onNavigate = LocalOnNavigate.current
val showDescription = rememberSaveable {
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
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.domain.util.BaseViewModel
import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject
@HiltViewModel
class HelpViewModel @Inject constructor(
) : ViewModel() {
) : BaseViewModel<HelpState, HelpEvent>() {
private val _state = MutableStateFlow(HelpState())
val state = _state.asStateFlow()
override val state = _state.asStateFlow()
override fun onEvent(event: HelpEvent) {}
fun init(screen: Screen.Help) {
viewModelScope.launch {

View file

@ -24,8 +24,6 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.components.CategoryTitle
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.components.CustomSearchTextField
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.customItems
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.screens.history.components.HistoryDeleteWholeHistoryDialog
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.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.LibraryViewModel
import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions
@Composable
fun HistoryScreenRoot() {
val navigator = LocalNavigator.current
val viewModel: HistoryViewModel = hiltViewModel()
val libraryViewModel: LibraryViewModel = hiltViewModel()
val onEvent = LocalHistoryViewModel.current.onEvent
val state = viewModel.state.collectAsState()
LaunchedEffect(Unit) {
onEvent(HistoryEvent.OnUpdateScrollOffset)
}
HistoryScreen(
state = state,
onNavigate = { navigator.it() },
onEvent = viewModel::onEvent,
onLibraryEvent = libraryViewModel::onEvent
)
HistoryScreen()
}
@OptIn(
@ -81,13 +71,13 @@ fun HistoryScreenRoot() {
ExperimentalMaterialApi::class
)
@Composable
private fun HistoryScreen(
state: State<HistoryState>,
onNavigate: OnNavigate,
onEvent: (HistoryEvent) -> Unit,
onLibraryEvent: (LibraryEvent) -> Unit
) {
private fun HistoryScreen() {
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(
refreshing = state.value.isRefreshing,
onRefresh = {
@ -97,15 +87,8 @@ private fun HistoryScreen(
val focusRequester = remember { FocusRequester() }
val snackbarState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
onEvent(HistoryEvent.OnUpdateScrollOffset)
}
if (state.value.showDeleteWholeHistoryDialog) {
HistoryDeleteWholeHistoryDialog(
onEvent = onEvent,
onLibraryLoadEvent = onLibraryEvent
)
HistoryDeleteWholeHistoryDialog()
}
Scaffold(
@ -304,15 +287,3 @@ private fun HistoryScreen(
}
}
}

View file

@ -6,6 +6,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.data.showToast
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.
*/
@Composable
fun HistoryDeleteWholeHistoryDialog(
onEvent: (HistoryEvent) -> Unit,
onLibraryLoadEvent: (LibraryEvent.OnLoadList) -> Unit
) {
fun HistoryDeleteWholeHistoryDialog() {
val context = LocalContext.current
val onEvent = LocalHistoryViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
CustomDialogWithContent(
title = stringResource(id = R.string.delete_history),
@ -34,7 +35,7 @@ fun HistoryDeleteWholeHistoryDialog(
onAction = {
onEvent(
HistoryEvent.OnDeleteWholeHistory {
onLibraryLoadEvent(LibraryEvent.OnLoadList)
onLibraryEvent(LibraryEvent.OnLoadList)
}
)
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.material3.SnackbarResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
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.GetHistory
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.presentation.data.Screen
import java.text.SimpleDateFormat
@ -36,10 +36,10 @@ class HistoryViewModel @Inject constructor(
private val deleteWholeHistory: DeleteWholeHistory,
private val deleteHistory: DeleteHistory,
private val getBooksById: GetBooksById
) : ViewModel() {
) : BaseViewModel<HistoryState, HistoryEvent>() {
private val _state = MutableStateFlow(HistoryState())
val state = _state.asStateFlow()
override val state = _state.asStateFlow()
private var job: 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) {
is HistoryEvent.OnRefreshList -> {
job3?.cancel()

View file

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

View file

@ -20,7 +20,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.CustomIconButton
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.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/**
* Library Top Bar.
* Contains [AnimatedTopAppBar] and [LibraryTabRow].
*
* @param state [LibraryState].
* @param pagerState [PagerState].
* @param onEvent [LibraryEvent] callback.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LibraryTopBar(
state: State<LibraryState>,
pagerState: PagerState,
onEvent: (LibraryEvent) -> Unit
) {
fun LibraryTopBar(pagerState: PagerState) {
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val focusRequester = remember { FocusRequester() }
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.outlined.DeleteOutline
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.data.showToast
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.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/**
* Delete dialog. Deletes all selected books.
*/
@Composable
fun LibraryDeleteDialog(
state: State<LibraryState>,
onEvent: (LibraryEvent) -> Unit,
onBrowseLoadEvent: (BrowseEvent.OnLoadList) -> Unit,
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit
) {
fun LibraryDeleteDialog() {
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(
title = stringResource(id = R.string.delete_books),
@ -40,8 +40,8 @@ fun LibraryDeleteDialog(
onAction = {
onEvent(
LibraryEvent.OnDeleteBooks {
onBrowseLoadEvent(BrowseEvent.OnLoadList)
onHistoryLoadEvent(HistoryEvent.OnLoadList)
onBrowseEvent(BrowseEvent.OnLoadList)
onHistoryEvent(HistoryEvent.OnLoadList)
}
)
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.automirrored.outlined.DriveFileMove
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.components.custom_dialog.SelectableDialogItem
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.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryState
/**
* Move dialog. Moves all selected books to the selected category.
*/
@Composable
fun LibraryMoveDialog(
state: State<LibraryState>,
onEvent: (LibraryEvent) -> Unit,
onHistoryLoadEvent: (HistoryEvent.OnLoadList) -> Unit,
pagerState: PagerState
) {
fun LibraryMoveDialog(pagerState: PagerState) {
val context = LocalContext.current
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
CustomDialogWithLazyColumn(
title = stringResource(id = R.string.move_books),
@ -44,7 +42,7 @@ fun LibraryMoveDialog(
LibraryEvent.OnMoveBooks(
pagerState,
refreshList = {
onHistoryLoadEvent(HistoryEvent.OnLoadList)
onHistoryEvent(HistoryEvent.OnLoadList)
}
)
)

View file

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

View file

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

View file

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

View file

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

View file

@ -23,6 +23,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.R
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.screens.reader.data.ReaderEvent
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 doubleClickTranslationEnabled Whether Double Click Translation is enabled.
* @param toolbarHidden Whether selection toolBar is hidden.
* @param onEvent [ReaderEvent] callback.
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
@ -60,9 +60,10 @@ fun LazyItemScope.ReaderTextParagraph(
sidePadding: Dp,
paragraphIndentation: Boolean,
doubleClickTranslationEnabled: Boolean,
toolbarHidden: Boolean,
onEvent: (ReaderEvent) -> Unit
toolbarHidden: Boolean
) {
val onEvent = LocalReaderViewModel.current.onEvent
Column(
modifier = Modifier
.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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.removeTrailingZero
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.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderState
import ua.acclorite.book_story.presentation.ui.Colors
/**
* 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
fun ReaderBottomBar(
state: State<ReaderState>,
onEvent: (ReaderEvent) -> Unit,
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
fun ReaderBottomBar() {
val state = LocalReaderViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val progress by remember(state.value.book.progress) {
derivedStateOf {
(state.value.book.progress * 100)
@ -87,8 +83,8 @@ fun ReaderBottomBar(
firstVisibleItemIndex = state.value.listState.firstVisibleItemIndex,
firstVisibleItemOffset = 0,
refreshList = { book ->
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(book))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(book))
onLibraryEvent(LibraryEvent.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.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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.unit.sp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomIconButton
import ua.acclorite.book_story.presentation.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.screens.history.data.HistoryEvent
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.ReaderState
import ua.acclorite.book_story.presentation.ui.Colors
/**
* Reader top bar. Displays title of the book.
*
* @param state [ReaderState].
* @param onNavigate Navigator callback.
* @param onEvent [ReaderEvent] callback.
* @param onLibraryUpdateEvent [LibraryEvent] callback.
* @param onHistoryUpdateEvent [HistoryEvent] callback.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReaderTopBar(
state: State<ReaderState>,
onNavigate: OnNavigate,
onEvent: (ReaderEvent) -> Unit,
onLibraryUpdateEvent: (LibraryEvent.OnUpdateBook) -> Unit,
onHistoryUpdateEvent: (HistoryEvent.OnUpdateBook) -> Unit
) {
fun ReaderTopBar() {
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 onNavigate = LocalOnNavigate.current
TopAppBar(
navigationIcon = {
@ -62,8 +56,8 @@ fun ReaderTopBar(
ReaderEvent.OnGoBack(
context = context,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
onNavigate {
@ -94,8 +88,8 @@ fun ReaderTopBar(
ReaderEvent.OnGoBack(
context = context,
refreshList = {
onLibraryUpdateEvent(LibraryEvent.OnUpdateBook(it))
onHistoryUpdateEvent(HistoryEvent.OnUpdateBook(it))
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
},
navigate = {
onNavigate {

View file

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

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
/**
@ -24,6 +25,9 @@ fun ReaderSettingsBottomSheetTabRow(
pagerState: PagerState
) {
val tabItems = listOf(
fun ReaderSettingsBottomSheetTabRow(pagerState: PagerState) {
val onEvent = LocalReaderViewModel.current.onEvent
stringResource(id = R.string.general_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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -33,16 +32,16 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.
*
* @param state [ReaderState].
*/
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ReaderStartItem(state: State<ReaderState>) {
fun ReaderStartItem() {
val state = LocalReaderViewModel.current.state
val statusBarHeight = WindowInsets
.statusBarsIgnoringVisibility
.asPaddingValues()

View file

@ -36,7 +36,7 @@ sealed class ReaderEvent {
data class OnScroll(val progress: Float) : 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(
val context: ComponentActivity,
val onUpdateCategories: (Book) -> Unit,

View file

@ -10,7 +10,6 @@ import androidx.compose.runtime.snapshotFlow
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
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.GetText
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.UIText
import ua.acclorite.book_story.presentation.data.Screen
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 kotlin.math.roundToInt
@ -48,14 +46,14 @@ class ReaderViewModel @Inject constructor(
private val getText: GetText,
private val getLatestHistory: GetLatestHistory,
private val getBookById: GetBookById
) : ViewModel() {
) : BaseViewModel<ReaderState, ReaderEvent>() {
private val _state = MutableStateFlow(ReaderState())
val state = _state.asStateFlow()
override val state = _state.asStateFlow()
private var eventJob = SupervisorJob()
fun onEvent(event: ReaderEvent) {
override fun onEvent(event: ReaderEvent) {
viewModelScope.launch(eventJob + Dispatchers.Main) {
when (event) {
is ReaderEvent.OnTextIsEmpty -> {
@ -269,7 +267,6 @@ class ReaderViewModel @Inject constructor(
launch(Dispatchers.IO) {
_state.update {
it.copy(
currentPage = if (it.showSettingsBottomSheet) it.currentPage else 0,
showSettingsBottomSheet = !it.showSettingsBottomSheet
)
}
@ -278,7 +275,13 @@ class ReaderViewModel @Inject constructor(
is ReaderEvent.OnScrollToSettingsPage -> {
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)
fun onUpdateProgress(
onLibraryEvent: (LibraryEvent) -> Unit,
onHistoryEvent: (HistoryEvent) -> Unit
) {
fun onUpdateProgress(refreshList: (Book) -> Unit) {
viewModelScope.launch {
snapshotFlow {
_state.value.listState.firstVisibleItemIndex to _state.value.listState.firstVisibleItemScrollOffset
@ -538,8 +538,7 @@ class ReaderViewModel @Inject constructor(
firstVisibleItemIndex = firstVisibleItemIndex,
firstVisibleItemOffset = firstVisibleItemScrollOffset,
refreshList = { book ->
onLibraryEvent(LibraryEvent.OnUpdateBook(book))
onHistoryEvent(HistoryEvent.OnUpdateBook(book))
refreshList(book)
}
)
)

View file

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

View file

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

View file

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

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.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color
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.ThemePreferencesSubcategory
@ -20,34 +15,21 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.c
* Appearance Settings Category.
* 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 topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied.
*/
fun LazyListScope.AppearanceSettingsCategory(
state: State<MainState>,
settingsState: State<SettingsState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp
) {
ThemePreferencesSubcategory(
state = state,
onMainEvent = onMainEvent,
titleColor = titleColor,
topPadding = topPadding,
bottomPadding = 0.dp
)
ColorsSubcategory(
state = state,
settingsState = settingsState,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent,
titleColor = titleColor,
topPadding = 22.dp,
bottomPadding = bottomPadding

View file

@ -36,7 +36,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.CustomAnimatedVisibility
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.screens.settings.components.ColorPickerWithTitle
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.Transitions
@ -78,13 +77,14 @@ import ua.acclorite.book_story.presentation.ui.Transitions
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ColorPresetSetting(
state: State<SettingsState>,
onEvent: (SettingsEvent) -> Unit,
backgroundColor: Color,
verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp
) {
val state = LocalSettingsViewModel.current.state
val onEvent = LocalSettingsViewModel.current.onEvent
val context = LocalContext.current
val reorderableListState = rememberReorderableLazyListState(
lazyListState = state.value.colorPresetsListState
) { 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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
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.
*/
@Composable
fun DarkThemeSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun DarkThemeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SegmentedButtonWithTitle(
title = stringResource(id = R.string.dark_theme_option),
buttons = DarkTheme.entries.map {

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun FastColorPresetChangeSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun FastColorPresetChangeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SwitchWithTitle(
selected = state.value.fastColorPresetChange,
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.MainState
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.PureDark
@ -18,10 +17,10 @@ import ua.acclorite.book_story.presentation.ui.isDark
* If true, enables Pure Dark (OLED) theme.
*/
@Composable
fun PureDarkSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun PureDarkSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
ExpandingTransition(visible = state.value.darkTheme.isDark()) {
SegmentedButtonWithTitle(
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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.MainState
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.ExpandingTransition
@ -25,10 +24,10 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
* Lets user change theme contrast levels.
*/
@Composable
fun ThemeContrastSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun ThemeContrastSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val themeContrastTheme = remember { mutableStateOf(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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.presentation.components.CategoryTitle
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.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.ThemeContrast
import ua.acclorite.book_story.presentation.ui.animatedColorScheme
@ -57,11 +56,12 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
*/
@Composable
fun ThemeSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp
) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val themes = remember {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Constants.THEMES
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.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
@ -17,10 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.FastColorPresetChangeSetting
@ -29,10 +24,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.c
* Contains all settings from Colors.
*/
fun LazyListScope.ColorsSubcategory(
state: State<MainState>,
settingsState: State<SettingsState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.colors_appearance_settings) },
backgroundColor: @Composable () -> Color = { MaterialTheme.colorScheme.surfaceContainerLow },
@ -59,18 +50,11 @@ fun LazyListScope.ColorsSubcategory(
}
item {
ColorPresetSetting(
state = settingsState,
backgroundColor = backgroundColor.invoke(),
onEvent = onSettingsEvent
)
ColorPresetSetting(backgroundColor = backgroundColor.invoke())
}
item {
FastColorPresetChangeSetting(
state = state,
onMainEvent = onMainEvent
)
FastColorPresetChangeSetting()
}
item {

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.PureDarkSetting
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.
*/
fun LazyListScope.ThemePreferencesSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.theme_appearance_settings) },
showTitle: Boolean = true,
@ -56,31 +51,19 @@ fun LazyListScope.ThemePreferencesSubcategory(
}
item {
DarkThemeSetting(
state = state,
onMainEvent = onMainEvent
)
DarkThemeSetting()
}
item {
ThemeSetting(
state = state,
onMainEvent = onMainEvent
)
ThemeSetting()
}
item {
ThemeContrastSetting(
state = state,
onMainEvent = onMainEvent
)
ThemeContrastSetting()
}
item {
PureDarkSetting(
state = state,
onMainEvent = onMainEvent
)
PureDarkSetting()
}
item {

View file

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

View file

@ -3,11 +3,8 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.State
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.BrowseGeneralSubcategory
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.
* Contains all Browse settings.
*
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied.
*/
fun LazyListScope.BrowseSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp
) {
BrowseGeneralSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = topPadding,
bottomPadding = 0.dp
)
BrowseFilterSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = 22.dp,
bottomPadding = 0.dp
)
BrowseSortSubcategory(
state = state,
onMainEvent = onMainEvent,
topPadding = 22.dp,
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
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.MainState
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
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.
*/
@Composable
fun BrowseFilesStructureSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun BrowseFilesStructureSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SegmentedButtonWithTitle(
title = stringResource(id = R.string.browse_files_structure_option),
buttons = BrowseFilesStructure.entries.map {

View file

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

View file

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

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.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.
*/
@Composable
fun BrowsePinFavoriteDirectoriesSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun BrowsePinFavoriteDirectoriesSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
ExpandingTransition(visible = state.value.browseFilesStructure == BrowseFilesStructure.DIRECTORIES) {
SwitchWithTitle(
selected = state.value.browsePinFavoriteDirectories,

View file

@ -17,7 +17,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.unit.dp
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.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseSortOrder
/**
* Browse Sort Order setting.
* Lets user choose how to sort files in Browse.
*/
fun LazyListScope.BrowseSortOrderSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun LazyListScope.BrowseSortOrderSetting() {
customItems(BrowseSortOrder.entries, key = { it.name }) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SortItem(
item = 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.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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
/**
@ -26,8 +23,6 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
* Contains all settings from Filter Browse settings.
*/
fun LazyListScope.BrowseFilterSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.filter_browse_settings) },
showTitle: Boolean = true,
@ -52,10 +47,7 @@ fun LazyListScope.BrowseFilterSubcategory(
}
}
BrowseFilterSetting(
state = state,
onMainEvent = onMainEvent
)
BrowseFilterSetting()
item {
Spacer(

View file

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
@ -17,8 +16,6 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
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.BrowseGridSizeSetting
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.
*/
fun LazyListScope.BrowseGeneralSubcategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
title: @Composable () -> String = { stringResource(id = R.string.general_browse_settings) },
showTitle: Boolean = true,
@ -56,31 +51,19 @@ fun LazyListScope.BrowseGeneralSubcategory(
}
item {
BrowseFilesStructureSetting(
state = state,
onMainEvent = onMainEvent
)
BrowseFilesStructureSetting()
}
item {
BrowsePinFavoriteDirectoriesSetting(
state = state,
onMainEvent = onMainEvent
)
BrowsePinFavoriteDirectoriesSetting()
}
item {
BrowseLayoutSetting(
state = state,
onMainEvent = onMainEvent
)
BrowseLayoutSetting()
}
item {
BrowseGridSizeSetting(
state = state,
onMainEvent = onMainEvent
)
BrowseGridSizeSetting()
}
item {

View file

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

View file

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

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.height
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.coerceAtLeast
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.CheckForUpdatesSetting
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.
* 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 bottomPadding Bottom padding to be applied.
*/
fun LazyListScope.GeneralSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit,
topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp
) {
@ -41,25 +31,15 @@ fun LazyListScope.GeneralSettingsCategory(
}
item {
AppLanguageSetting(
state = state,
onMainEvent = onMainEvent
)
AppLanguageSetting()
}
item {
CheckForUpdatesSetting(
state = state,
onMainEvent = onMainEvent,
onSettingsEvent = onSettingsEvent
)
CheckForUpdatesSetting()
}
item {
DoublePressExitSetting(
state = state,
onMainEvent = onMainEvent
)
DoublePressExitSetting()
}
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
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
/**
@ -16,10 +15,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
* Changes app's language.
*/
@Composable
fun AppLanguageSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun AppLanguageSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
ChipsWithTitle(
title = stringResource(id = R.string.language_option),
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.filled.Security
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.rememberPermissionState
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.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.data.SettingsEvent
@ -27,12 +27,12 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
@OptIn(ExperimentalPermissionsApi::class)
@SuppressLint("InlinedApi")
@Composable
fun CheckForUpdatesSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
onSettingsEvent: (SettingsEvent) -> Unit
) {
fun CheckForUpdatesSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val onSettingsEvent = LocalSettingsViewModel.current.onEvent
val activity = LocalContext.current as ComponentActivity
val notificationsPermissionState = rememberPermissionState(
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 androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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")
@Composable
fun DoublePressExitSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun DoublePressExitSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SwitchWithTitle(
selected = state.value.doublePressExit,
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.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.components.GoBackButton
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.data.LocalOnNavigate
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.ReaderSettingsCategory
@Composable
fun ReaderSettingsRoot() {
val navigator = LocalNavigator.current
val mainViewModel: MainViewModel = hiltViewModel()
val state = mainViewModel.state.collectAsState()
ReaderSettings(
state = state,
onNavigate = { navigator.it() },
onMainEvent = mainViewModel::onEvent
)
ReaderSettings()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ReaderSettings(
state: State<MainState>,
onNavigate: OnNavigate,
onMainEvent: (MainEvent) -> Unit
) {
private fun ReaderSettings() {
val onNavigate = LocalOnNavigate.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
Scaffold(
@ -80,10 +61,7 @@ private fun ReaderSettings(
.padding(top = paddingValues.calculateTopPadding()),
state = scrollState.second
) {
ReaderSettingsCategory(
state = state,
onMainEvent = onMainEvent
)
ReaderSettingsCategory()
}
}
}

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.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color
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.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.TextSubcategory
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.
* Contains all Reader settings.
*
* @param state [MainState] instance.
* @param onMainEvent [MainEvent] callback.
* @param titleColor Color that [CategoryTitle] has.
* @param topPadding Top padding to be applied.
* @param bottomPadding Bottom padding to be applied.
*/
fun LazyListScope.ReaderSettingsCategory(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit,
titleColor: @Composable () -> Color = { MaterialTheme.colorScheme.primary },
topPadding: Dp = 16.dp,
bottomPadding: Dp = 48.dp
) {
FontSubcategory(
state = state,
onMainEvent = onMainEvent,
titleColor = titleColor,
topPadding = topPadding,
bottomPadding = 0.dp
)
TextSubcategory(
state,
onMainEvent = onMainEvent,
titleColor = titleColor,
topPadding = 22.dp,
bottomPadding = 0.dp
)
TranslatorSubcategory(
state,
onMainEvent = onMainEvent,
titleColor = titleColor,
topPadding = 22.dp,
bottomPadding = bottomPadding

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun DoubleClickTranslationSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun DoubleClickTranslationSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SwitchWithTitle(
selected = state.value.doubleClickTranslation,
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
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
/**
@ -17,10 +16,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
* Changes Reader's font, uses fonts from [Constants.FONTS].
*/
@Composable
fun FontFamilySetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun FontFamilySetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find {
it.id == state.value.fontFamily

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun FontSizeSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun FontSizeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SliderWithTitle(
value = state.value.fontSize to "pt",
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.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.components.LocalMainViewModel
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
/**
@ -18,10 +17,10 @@ import ua.acclorite.book_story.presentation.screens.settings.components.Segmente
* Changes Reader's font style (Normal/Italic).
*/
@Composable
fun FontStyleSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun FontStyleSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find {
it.id == state.value.fontFamily

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun LetterSpacingSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun LetterSpacingSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SliderWithTitle(
value = state.value.letterSpacing to "pt",
fromValue = -8,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun LineHeightSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun LineHeightSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SliderWithTitle(
value = state.value.lineHeight to "pt",
fromValue = 1,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun ParagraphHeightSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun ParagraphHeightSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SliderWithTitle(
value = state.value.paragraphHeight to "pt",
fromValue = 0,

View file

@ -1,11 +1,10 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.res.stringResource
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.MainState
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.
*/
@Composable
fun ParagraphIndentationSetting(
state: State<MainState>,
onMainEvent: (MainEvent) -> Unit
) {
fun ParagraphIndentationSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
SwitchWithTitle(
selected = state.value.paragraphIndentation,
title = stringResource(id = R.string.paragraph_indentation_option)

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