🛠️ ViewModel accessing reformat

* Reformatted a way to get State and Event in Composables
This commit is contained in:
Acclorite 2024-10-16 23:10:35 +03:00
parent 119ff0b956
commit 4a31a916bf
112 changed files with 1154 additions and 1060 deletions

View file

@ -10,7 +10,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -18,12 +17,13 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.bottom_navigation_bar.BottomNavigationBar
import ua.acclorite.book_story.presentation.core.components.custom_navigation_rail.CustomNavigationRail
import ua.acclorite.book_story.presentation.core.navigation.NavigationHost
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.about.AboutScreenRoot
import ua.acclorite.book_story.presentation.screens.about.nested.credits.CreditsScreenRoot
@ -76,7 +76,12 @@ class Activity : AppCompatActivity() {
}
// Initializing all variables
mainViewModel.init(libraryViewModel, settingsViewModel)
mainViewModel.onEvent(
MainEvent.OnInit(
libraryViewModelReady = libraryViewModel.isReady,
settingsViewModelReady = settingsViewModel.isReady
)
)
// Splash screen
installSplashScreen().apply {
@ -90,8 +95,8 @@ class Activity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val state = LocalMainViewModel.current.state
val isLoaded = mainViewModel.isReady.collectAsState()
val state = MainViewModel.getState()
val isLoaded = mainViewModel.isReady.collectAsStateWithLifecycle()
val density = LocalDensity.current
val imeInsets = WindowInsets.ime

View file

@ -1,91 +0,0 @@
package ua.acclorite.book_story.presentation.core.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.presentation.core.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

@ -174,7 +174,7 @@ class Navigator @AssistedInject constructor(
*
* @param screen [Screen].
*/
fun putScreen(screen: Screen) {
fun putScreen(screen: Screen) = viewModelScope.launch(Dispatchers.Main.immediate) {
var found = false
for ((index, arg) in screens.value.withIndex()) {
@ -219,7 +219,7 @@ class Navigator @AssistedInject constructor(
screen: Screen,
useBackAnimation: Boolean = false,
saveInBackStack: Boolean = true
) = viewModelScope.launch(Dispatchers.Default) {
) = viewModelScope.launch(Dispatchers.Main.immediate) {
if (screen.getRoute() == currentScreen.value) {
return@launch
}
@ -242,7 +242,7 @@ class Navigator @AssistedInject constructor(
*/
fun navigateBack(
useBackAnimation: Boolean = true
) = viewModelScope.launch(Dispatchers.Default) {
) = viewModelScope.launch(Dispatchers.Main.immediate) {
if (canGoBack()) {
savedStateHandle[USE_BACK_ANIM] = useBackAnimation
savedStateHandle[CURRENT_SCREEN] = backStack.value.last()

View file

@ -1,9 +0,0 @@
package ua.acclorite.book_story.presentation.core.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

@ -20,7 +20,6 @@ fun Intent.launchActivity(
activity: ComponentActivity,
createChooser: Boolean = false,
openInNewWindow: Boolean = true,
success: (() -> Unit)? = null,
error: () -> Unit
) {
val intent = if (createChooser) Intent.createChooser(this, "") else this
@ -35,8 +34,6 @@ fun Intent.launchActivity(
error()
return
}
success?.invoke()
}
fun Float.calculateProgress(digits: Int): String {

View file

@ -0,0 +1,26 @@
package ua.acclorite.book_story.presentation.core.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.StateFlow
abstract class UiViewModel<S, E> : ViewModel() {
abstract val state: StateFlow<S>
abstract fun onEvent(event: E)
companion object {
@Composable
inline fun <reified V : UiViewModel<S, E>, S, E> getState(): State<S> {
return hiltViewModel<V>().state.collectAsStateWithLifecycle()
}
@Composable
inline fun <reified V : UiViewModel<S, E>, S, E> getEvent(): (E) -> Unit {
return hiltViewModel<V>()::onEvent
}
}
}

View file

@ -1,9 +1,15 @@
package ua.acclorite.book_story.presentation.data
import androidx.compose.runtime.Immutable
import kotlinx.coroutines.flow.StateFlow
@Immutable
sealed class MainEvent {
data class OnInit(
val libraryViewModelReady: StateFlow<Boolean>,
val settingsViewModelReady: StateFlow<Boolean>
) : MainEvent()
data class OnChangeLanguage(val value: String) : MainEvent()
data class OnChangeTheme(val value: String) : MainEvent()
data class OnChangeDarkTheme(val value: String) : MainEvent()

View file

@ -1,5 +1,6 @@
package ua.acclorite.book_story.presentation.data
import androidx.compose.runtime.Composable
import androidx.datastore.preferences.core.Preferences
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
@ -12,16 +13,13 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ua.acclorite.book_story.domain.use_case.data_store.ChangeLanguage
import ua.acclorite.book_story.domain.use_case.data_store.GetAllSettings
import ua.acclorite.book_story.domain.use_case.data_store.SetDatastore
import ua.acclorite.book_story.domain.use_case.remote.CheckForUpdates
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.constants.DataStoreConstants
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.toBrowseFilesStructure
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.toBrowseLayout
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.toBrowseSortOrder
@ -42,7 +40,15 @@ class MainViewModel @Inject constructor(
private val changeLanguage: ChangeLanguage,
private val checkForUpdates: CheckForUpdates,
private val getAllSettings: GetAllSettings
) : BaseViewModel<MainState, MainEvent>() {
) : UiViewModel<MainState, MainEvent>() {
companion object {
@Composable
fun getState() = getState<MainViewModel, MainState, MainEvent>()
@Composable
fun getEvent() = getEvent<MainViewModel, MainState, MainEvent>()
}
private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow()
@ -56,320 +62,317 @@ class MainViewModel @Inject constructor(
override val state = _state.asStateFlow()
override fun onEvent(event: MainEvent) {
viewModelScope.launch(Dispatchers.IO) {
when (event) {
is MainEvent.OnChangeLanguage -> handleLanguageUpdate(event.value)
when (event) {
is MainEvent.OnInit -> init(event)
is MainEvent.OnChangeDarkTheme -> handleDatastoreUpdate(
key = DataStoreConstants.DARK_THEME,
value = event.value,
updateState = {
it.copy(darkTheme = toDarkTheme())
}
)
is MainEvent.OnChangeLanguage -> handleLanguageUpdate(event)
is MainEvent.OnChangePureDark -> handleDatastoreUpdate(
key = DataStoreConstants.PURE_DARK,
value = event.value,
updateState = {
it.copy(pureDark = toPureDark())
}
)
is MainEvent.OnChangeDarkTheme -> handleDatastoreUpdate(
key = DataStoreConstants.DARK_THEME,
value = event.value,
updateState = {
it.copy(darkTheme = toDarkTheme())
}
)
is MainEvent.OnChangeThemeContrast -> handleDatastoreUpdate(
key = DataStoreConstants.THEME_CONTRAST,
value = event.value,
updateState = {
it.copy(themeContrast = toThemeContrast())
}
)
is MainEvent.OnChangePureDark -> handleDatastoreUpdate(
key = DataStoreConstants.PURE_DARK,
value = event.value,
updateState = {
it.copy(pureDark = toPureDark())
}
)
is MainEvent.OnChangeTheme -> handleDatastoreUpdate(
key = DataStoreConstants.THEME,
value = event.value,
updateState = {
it.copy(theme = toTheme())
}
)
is MainEvent.OnChangeThemeContrast -> handleDatastoreUpdate(
key = DataStoreConstants.THEME_CONTRAST,
value = event.value,
updateState = {
it.copy(themeContrast = toThemeContrast())
}
)
is MainEvent.OnChangeFontFamily -> handleDatastoreUpdate(
key = DataStoreConstants.FONT,
value = event.value,
updateState = {
it.copy(
fontFamily = Constants.FONTS.find { font -> font.id == event.value }?.id
?: Constants.FONTS[0].id
)
}
)
is MainEvent.OnChangeTheme -> handleDatastoreUpdate(
key = DataStoreConstants.THEME,
value = event.value,
updateState = {
it.copy(theme = toTheme())
}
)
is MainEvent.OnChangeFontStyle -> handleDatastoreUpdate(
key = DataStoreConstants.IS_ITALIC,
value = event.value,
updateState = {
it.copy(isItalic = this)
}
)
is MainEvent.OnChangeFontFamily -> handleDatastoreUpdate(
key = DataStoreConstants.FONT,
value = event.value,
updateState = {
it.copy(
fontFamily = Constants.FONTS.find { font -> font.id == event.value }?.id
?: Constants.FONTS[0].id
)
}
)
is MainEvent.OnChangeFontSize -> handleDatastoreUpdate(
key = DataStoreConstants.FONT_SIZE,
value = event.value,
updateState = {
it.copy(fontSize = this)
}
)
is MainEvent.OnChangeFontStyle -> handleDatastoreUpdate(
key = DataStoreConstants.IS_ITALIC,
value = event.value,
updateState = {
it.copy(isItalic = this)
}
)
is MainEvent.OnChangeLineHeight -> handleDatastoreUpdate(
key = DataStoreConstants.LINE_HEIGHT,
value = event.value,
updateState = {
it.copy(lineHeight = this)
}
)
is MainEvent.OnChangeFontSize -> handleDatastoreUpdate(
key = DataStoreConstants.FONT_SIZE,
value = event.value,
updateState = {
it.copy(fontSize = this)
}
)
is MainEvent.OnChangeParagraphHeight -> handleDatastoreUpdate(
key = DataStoreConstants.PARAGRAPH_HEIGHT,
value = event.value,
updateState = {
it.copy(paragraphHeight = this)
}
)
is MainEvent.OnChangeLineHeight -> handleDatastoreUpdate(
key = DataStoreConstants.LINE_HEIGHT,
value = event.value,
updateState = {
it.copy(lineHeight = this)
}
)
is MainEvent.OnChangeParagraphIndentation -> handleDatastoreUpdate(
key = DataStoreConstants.PARAGRAPH_INDENTATION,
value = event.value,
updateState = {
it.copy(paragraphIndentation = this)
}
)
is MainEvent.OnChangeParagraphHeight -> handleDatastoreUpdate(
key = DataStoreConstants.PARAGRAPH_HEIGHT,
value = event.value,
updateState = {
it.copy(paragraphHeight = this)
}
)
is MainEvent.OnChangeShowStartScreen -> handleDatastoreUpdate(
key = DataStoreConstants.SHOW_START_SCREEN,
value = event.value,
updateState = {
it.copy(showStartScreen = this)
}
)
is MainEvent.OnChangeParagraphIndentation -> handleDatastoreUpdate(
key = DataStoreConstants.PARAGRAPH_INDENTATION,
value = event.value,
updateState = {
it.copy(paragraphIndentation = this)
}
)
is MainEvent.OnChangeCheckForUpdates -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_UPDATES,
value = event.value,
updateState = {
it.copy(checkForUpdates = this)
}
)
is MainEvent.OnChangeShowStartScreen -> handleDatastoreUpdate(
key = DataStoreConstants.SHOW_START_SCREEN,
value = event.value,
updateState = {
it.copy(showStartScreen = this)
}
)
is MainEvent.OnChangeSidePadding -> handleDatastoreUpdate(
key = DataStoreConstants.SIDE_PADDING,
value = event.value,
updateState = {
it.copy(sidePadding = this)
}
)
is MainEvent.OnChangeCheckForUpdates -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_UPDATES,
value = event.value,
updateState = {
it.copy(checkForUpdates = this)
}
)
is MainEvent.OnChangeDoubleClickTranslation -> handleDatastoreUpdate(
key = DataStoreConstants.DOUBLE_CLICK_TRANSLATION,
value = event.value,
updateState = {
it.copy(doubleClickTranslation = this)
}
)
is MainEvent.OnChangeSidePadding -> handleDatastoreUpdate(
key = DataStoreConstants.SIDE_PADDING,
value = event.value,
updateState = {
it.copy(sidePadding = this)
}
)
is MainEvent.OnChangeFastColorPresetChange -> handleDatastoreUpdate(
key = DataStoreConstants.FAST_COLOR_PRESET_CHANGE,
value = event.value,
updateState = {
it.copy(fastColorPresetChange = this)
}
)
is MainEvent.OnChangeDoubleClickTranslation -> handleDatastoreUpdate(
key = DataStoreConstants.DOUBLE_CLICK_TRANSLATION,
value = event.value,
updateState = {
it.copy(doubleClickTranslation = this)
}
)
is MainEvent.OnChangeBrowseFilesStructure -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_FILES_STRUCTURE,
value = event.value,
updateState = {
it.copy(browseFilesStructure = toBrowseFilesStructure())
}
)
is MainEvent.OnChangeFastColorPresetChange -> handleDatastoreUpdate(
key = DataStoreConstants.FAST_COLOR_PRESET_CHANGE,
value = event.value,
updateState = {
it.copy(fastColorPresetChange = this)
}
)
is MainEvent.OnChangeBrowseLayout -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_LAYOUT,
value = event.value,
updateState = {
it.copy(browseLayout = toBrowseLayout())
}
)
is MainEvent.OnChangeBrowseFilesStructure -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_FILES_STRUCTURE,
value = event.value,
updateState = {
it.copy(browseFilesStructure = toBrowseFilesStructure())
}
)
is MainEvent.OnChangeBrowseAutoGridSize -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_AUTO_GRID_SIZE,
value = event.value,
updateState = {
it.copy(browseAutoGridSize = this)
}
)
is MainEvent.OnChangeBrowseLayout -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_LAYOUT,
value = event.value,
updateState = {
it.copy(browseLayout = toBrowseLayout())
}
)
is MainEvent.OnChangeBrowseGridSize -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_GRID_SIZE,
value = event.value,
updateState = {
it.copy(browseGridSize = this)
}
)
is MainEvent.OnChangeBrowseAutoGridSize -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_AUTO_GRID_SIZE,
value = event.value,
updateState = {
it.copy(browseAutoGridSize = this)
}
)
is MainEvent.OnChangeBrowsePinFavoriteDirectories -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_PIN_FAVORITE_DIRECTORIES,
value = event.value,
updateState = {
it.copy(browsePinFavoriteDirectories = this)
}
)
is MainEvent.OnChangeBrowseGridSize -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_GRID_SIZE,
value = event.value,
updateState = {
it.copy(browseGridSize = this)
}
)
is MainEvent.OnChangeBrowseSortOrder -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_SORT_ORDER,
value = event.value,
updateState = {
it.copy(browseSortOrder = toBrowseSortOrder())
}
)
is MainEvent.OnChangeBrowsePinFavoriteDirectories -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_PIN_FAVORITE_DIRECTORIES,
value = event.value,
updateState = {
it.copy(browsePinFavoriteDirectories = this)
}
)
is MainEvent.OnChangeBrowseSortOrderDescending -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_SORT_ORDER_DESCENDING,
value = event.value,
updateState = {
it.copy(browseSortOrderDescending = this)
}
)
is MainEvent.OnChangeBrowseSortOrder -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_SORT_ORDER,
value = event.value,
updateState = {
it.copy(browseSortOrder = toBrowseSortOrder())
}
)
is MainEvent.OnChangeBrowseIncludedFilterItem -> handleBrowseIncludedFilterItemUpdate(
event.value
)
is MainEvent.OnChangeBrowseSortOrderDescending -> handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_SORT_ORDER_DESCENDING,
value = event.value,
updateState = {
it.copy(browseSortOrderDescending = this)
}
)
is MainEvent.OnChangeTextAlignment -> handleDatastoreUpdate(
key = DataStoreConstants.TEXT_ALIGNMENT,
value = event.value,
updateState = {
it.copy(textAlignment = toTextAlignment())
}
)
is MainEvent.OnChangeBrowseIncludedFilterItem -> handleBrowseIncludedFilterItemUpdate(
event
)
is MainEvent.OnChangeDoublePressExit -> handleDatastoreUpdate(
key = DataStoreConstants.DOUBLE_PRESS_EXIT,
value = event.value,
updateState = {
it.copy(doublePressExit = this)
}
)
is MainEvent.OnChangeTextAlignment -> handleDatastoreUpdate(
key = DataStoreConstants.TEXT_ALIGNMENT,
value = event.value,
updateState = {
it.copy(textAlignment = toTextAlignment())
}
)
is MainEvent.OnChangeLetterSpacing -> handleDatastoreUpdate(
key = DataStoreConstants.LETTER_SPACING,
value = event.value,
updateState = {
it.copy(letterSpacing = this)
}
)
is MainEvent.OnChangeDoublePressExit -> handleDatastoreUpdate(
key = DataStoreConstants.DOUBLE_PRESS_EXIT,
value = event.value,
updateState = {
it.copy(doublePressExit = this)
}
)
is MainEvent.OnChangeAbsoluteDark -> handleDatastoreUpdate(
key = DataStoreConstants.ABSOLUTE_DARK,
value = event.value,
updateState = {
it.copy(absoluteDark = this)
}
)
is MainEvent.OnChangeLetterSpacing -> handleDatastoreUpdate(
key = DataStoreConstants.LETTER_SPACING,
value = event.value,
updateState = {
it.copy(letterSpacing = this)
}
)
is MainEvent.OnChangeCutoutPadding -> handleDatastoreUpdate(
key = DataStoreConstants.CUTOUT_PADDING,
value = event.value,
updateState = {
it.copy(cutoutPadding = this)
}
)
is MainEvent.OnChangeAbsoluteDark -> handleDatastoreUpdate(
key = DataStoreConstants.ABSOLUTE_DARK,
value = event.value,
updateState = {
it.copy(absoluteDark = this)
}
)
is MainEvent.OnChangeFullscreen -> handleDatastoreUpdate(
key = DataStoreConstants.FULLSCREEN,
value = event.value,
updateState = {
it.copy(fullscreen = this)
}
)
is MainEvent.OnChangeCutoutPadding -> handleDatastoreUpdate(
key = DataStoreConstants.CUTOUT_PADDING,
value = event.value,
updateState = {
it.copy(cutoutPadding = this)
}
)
is MainEvent.OnChangeKeepScreenOn -> handleDatastoreUpdate(
key = DataStoreConstants.KEEP_SCREEN_ON,
value = event.value,
updateState = {
it.copy(keepScreenOn = this)
}
)
is MainEvent.OnChangeFullscreen -> handleDatastoreUpdate(
key = DataStoreConstants.FULLSCREEN,
value = event.value,
updateState = {
it.copy(fullscreen = this)
}
)
is MainEvent.OnChangeVerticalPadding -> handleDatastoreUpdate(
key = DataStoreConstants.VERTICAL_PADDING,
value = event.value,
updateState = {
it.copy(verticalPadding = this)
}
)
is MainEvent.OnChangeKeepScreenOn -> handleDatastoreUpdate(
key = DataStoreConstants.KEEP_SCREEN_ON,
value = event.value,
updateState = {
it.copy(keepScreenOn = this)
}
)
is MainEvent.OnChangeHideBarsOnFastScroll -> handleDatastoreUpdate(
key = DataStoreConstants.HIDE_BARS_ON_FAST_SCROLL,
value = event.value,
updateState = {
it.copy(hideBarsOnFastScroll = this)
}
)
is MainEvent.OnChangeVerticalPadding -> handleDatastoreUpdate(
key = DataStoreConstants.VERTICAL_PADDING,
value = event.value,
updateState = {
it.copy(verticalPadding = this)
}
)
is MainEvent.OnChangePerceptionExpander -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER,
value = event.value,
updateState = {
it.copy(perceptionExpander = this)
}
)
is MainEvent.OnChangeHideBarsOnFastScroll -> handleDatastoreUpdate(
key = DataStoreConstants.HIDE_BARS_ON_FAST_SCROLL,
value = event.value,
updateState = {
it.copy(hideBarsOnFastScroll = this)
}
)
is MainEvent.OnChangePerceptionExpanderPadding -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER_PADDING,
value = event.value,
updateState = {
it.copy(perceptionExpanderPadding = this)
}
)
is MainEvent.OnChangePerceptionExpander -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER,
value = event.value,
updateState = {
it.copy(perceptionExpander = this)
}
)
is MainEvent.OnChangePerceptionExpanderThickness -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER_THICKNESS,
value = event.value,
updateState = {
it.copy(perceptionExpanderThickness = this)
}
)
is MainEvent.OnChangePerceptionExpanderPadding -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER_PADDING,
value = event.value,
updateState = {
it.copy(perceptionExpanderPadding = this)
}
)
is MainEvent.OnChangeCheckForTextUpdate -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_TEXT_UPDATE,
value = event.value,
updateState = {
it.copy(checkForTextUpdate = this)
}
)
is MainEvent.OnChangePerceptionExpanderThickness -> handleDatastoreUpdate(
key = DataStoreConstants.PERCEPTION_EXPANDER_THICKNESS,
value = event.value,
updateState = {
it.copy(perceptionExpanderThickness = this)
}
)
is MainEvent.OnChangeCheckForTextUpdateToast -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_TEXT_UPDATE_TOAST,
value = event.value,
updateState = {
it.copy(checkForTextUpdateToast = this)
}
)
is MainEvent.OnChangeCheckForTextUpdate -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_TEXT_UPDATE,
value = event.value,
updateState = {
it.copy(checkForTextUpdate = this)
}
)
is MainEvent.OnChangeScreenOrientation -> handleDatastoreUpdate(
key = DataStoreConstants.SCREEN_ORIENTATION,
value = event.value,
updateState = {
it.copy(screenOrientation = toReaderScreenOrientation())
}
)
}
is MainEvent.OnChangeCheckForTextUpdateToast -> handleDatastoreUpdate(
key = DataStoreConstants.CHECK_FOR_TEXT_UPDATE_TOAST,
value = event.value,
updateState = {
it.copy(checkForTextUpdateToast = this)
}
)
is MainEvent.OnChangeScreenOrientation -> handleDatastoreUpdate(
key = DataStoreConstants.SCREEN_ORIENTATION,
value = event.value,
updateState = {
it.copy(screenOrientation = toReaderScreenOrientation())
}
)
}
}
fun init(
libraryViewModel: LibraryViewModel,
settingsViewModel: SettingsViewModel,
) {
private fun init(event: MainEvent.OnInit) {
viewModelScope.launch(Dispatchers.Main) {
val settings = getAllSettings.execute()
@ -391,8 +394,8 @@ class MainViewModel @Inject constructor(
viewModelScope.launch(Dispatchers.IO) {
combine(
libraryViewModel.isReady,
settingsViewModel.isReady
event.libraryViewModelReady,
event.settingsViewModelReady
) { (libraryViewModelReady, settingsViewModelReady) ->
libraryViewModelReady && settingsViewModelReady
}.collectLatest { ready ->
@ -421,19 +424,21 @@ class MainViewModel @Inject constructor(
}
}
private suspend fun handleLanguageUpdate(value: String) {
withContext(Dispatchers.Main) {
changeLanguage.execute(value)
private fun handleLanguageUpdate(event: MainEvent.OnChangeLanguage) {
viewModelScope.launch(Dispatchers.Main) {
changeLanguage.execute(event.value)
updateStateWithSavedHandle {
it.copy(language = value)
it.copy(language = event.value)
}
}
}
private suspend fun handleBrowseIncludedFilterItemUpdate(value: String) {
private fun handleBrowseIncludedFilterItemUpdate(
event: MainEvent.OnChangeBrowseIncludedFilterItem
) {
val set = _state.value.browseIncludedFilterItems.toMutableSet()
if (!set.add(value)) {
set.remove(value)
if (!set.add(event.value)) {
set.remove(event.value)
}
handleDatastoreUpdate(
key = DataStoreConstants.BROWSE_INCLUDED_FILTER_ITEMS,
@ -444,6 +449,22 @@ class MainViewModel @Inject constructor(
)
}
/**
* Handles and updates Datastore.
*/
private fun <V> handleDatastoreUpdate(
key: Preferences.Key<V>,
value: V,
updateState: V.(MainState) -> MainState
) {
viewModelScope.launch(Dispatchers.IO) {
setDatastore.execute(key = key, value = value)
updateStateWithSavedHandle {
value.updateState(it)
}
}
}
/**
* Updates [MainState] along with [SavedStateHandle].
*/
@ -455,20 +476,4 @@ class MainViewModel @Inject constructor(
function(it)
}
}
/**
* Handles and updates Datastore.
*/
private suspend fun <V> handleDatastoreUpdate(
key: Preferences.Key<V>,
value: V,
updateState: V.(MainState) -> MainState
) {
withContext(Dispatchers.IO) {
setDatastore.execute(key = key, value = value)
updateStateWithSavedHandle {
value.updateState(it)
}
}
}
}

View file

@ -30,7 +30,6 @@ import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.core.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
@ -40,6 +39,7 @@ 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.AboutViewModel
@Composable
fun AboutScreenRoot() {
@ -49,8 +49,8 @@ fun AboutScreenRoot() {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AboutScreen() {
val state = LocalAboutViewModel.current.state
val onEvent = LocalAboutViewModel.current.onEvent
val state = AboutViewModel.getState()
val onEvent = AboutViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -7,19 +7,19 @@ 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.core.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
/**
* Update dialog. If there is app update transfers to the download page.
*/
@Composable
fun AboutUpdateDialog() {
val state = LocalAboutViewModel.current.state
val onEvent = LocalAboutViewModel.current.onEvent
val state = AboutViewModel.getState()
val onEvent = AboutViewModel.getEvent()
val context = LocalContext.current
val update = remember(state.value.showUpdateDialog) {

View file

@ -13,10 +13,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
/**
* About Badges.
@ -26,7 +26,7 @@ import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
*/
@Composable
fun AboutBadges(verticalPadding: Dp = 18.dp) {
val onEvent = LocalAboutViewModel.current.onEvent
val onEvent = AboutViewModel.getEvent()
val context = LocalContext.current
Box(

View file

@ -3,6 +3,7 @@ package ua.acclorite.book_story.presentation.screens.about.data
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@ -11,14 +12,22 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.use_case.remote.CheckForUpdates
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.core.util.launchActivity
import javax.inject.Inject
@HiltViewModel
class AboutViewModel @Inject constructor(
private val checkForUpdates: CheckForUpdates,
) : BaseViewModel<AboutState, AboutEvent>() {
) : UiViewModel<AboutState, AboutEvent>() {
companion object {
@Composable
fun getState() = getState<AboutViewModel, AboutState, AboutEvent>()
@Composable
fun getEvent() = getEvent<AboutViewModel, AboutState, AboutEvent>()
}
private val _state = MutableStateFlow(AboutState())
override val state = _state.asStateFlow()
@ -91,11 +100,3 @@ class AboutViewModel @Inject constructor(
}
}
}

View file

@ -20,12 +20,12 @@ import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalAboutViewModel
import ua.acclorite.book_story.presentation.core.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.util.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
@ -36,7 +36,7 @@ fun CreditsScreenRoot() {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CreditsScreen() {
val onEvent = LocalAboutViewModel.current.onEvent
val onEvent = AboutViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -36,26 +36,32 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalLicenseInfoViewModel
import ua.acclorite.book_story.presentation.core.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.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.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 viewModel = LocalLicenseInfoViewModel.current.viewModel
val onEvent = LicenseInfoViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.init(
screen = screen,
onNavigate = onNavigate,
context = context
onEvent(
LicenseInfoEvent.OnInit(
screen = screen,
navigateBack = {
onNavigate {
navigateBack()
}
},
context = context
)
)
}
@ -65,8 +71,8 @@ fun LicenseInfoScreenRoot(screen: Screen.About.LicenseInfo) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LicenseInfoScreen() {
val state = LocalLicenseInfoViewModel.current.state
val onEvent = LocalLicenseInfoViewModel.current.onEvent
val state = LicenseInfoViewModel.getState()
val onEvent = LicenseInfoViewModel.getEvent()
val context = LocalContext.current
val onNavigate = LocalNavigator.current

View file

@ -2,9 +2,16 @@ package ua.acclorite.book_story.presentation.screens.about.nested.license_info.d
import android.content.Context
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.presentation.core.navigation.Screen
@Immutable
sealed class LicenseInfoEvent {
data class OnInit(
val screen: Screen.About.LicenseInfo,
val navigateBack: () -> Unit,
val context: Context
) : LicenseInfoEvent()
data class OnOpenLicensePage(
val page: String,
val context: Context,

View file

@ -1,9 +1,9 @@
package ua.acclorite.book_story.presentation.screens.about.nested.license_info.data
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withContext
@ -13,51 +13,55 @@ 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.OnNavigate
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.core.util.launchActivity
import javax.inject.Inject
@HiltViewModel
class LicenseInfoViewModel @Inject constructor(
) : BaseViewModel<LicenseInfoState, LicenseInfoEvent>() {
) : UiViewModel<LicenseInfoState, LicenseInfoEvent>() {
companion object {
@Composable
fun getState() = getState<LicenseInfoViewModel, LicenseInfoState, LicenseInfoEvent>()
@Composable
fun getEvent() = getEvent<LicenseInfoViewModel, LicenseInfoState, LicenseInfoEvent>()
}
private val _state = MutableStateFlow(LicenseInfoState())
override val state = _state.asStateFlow()
override fun onEvent(event: LicenseInfoEvent) {
when (event) {
is LicenseInfoEvent.OnOpenLicensePage -> {
viewModelScope.launch {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(event.page)
)
is LicenseInfoEvent.OnInit -> init(event)
intent.launchActivity(event.context as ComponentActivity) {
event.noAppsFound()
}
is LicenseInfoEvent.OnOpenLicensePage -> {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(event.page)
)
intent.launchActivity(event.context as ComponentActivity) {
event.noAppsFound()
}
}
}
}
fun init(screen: Screen.About.LicenseInfo, onNavigate: OnNavigate, context: Context) {
private fun init(event: LicenseInfoEvent.OnInit) {
viewModelScope.launch(Dispatchers.IO) {
_state.update {
it.copy(license = null)
}
val license = Libs.Builder().withContext(context).build().libraries.find {
it.uniqueId == screen.licenseId
val license = Libs.Builder().withContext(event.context).build().libraries.find {
it.uniqueId == event.screen.licenseId
}
if (license == null) {
onNavigate {
navigateBack()
}
event.navigateBack()
return@launch
}

View file

@ -28,19 +28,20 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalLicensesViewModel
import ua.acclorite.book_story.presentation.core.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.components.LicenseItem
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesEvent
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesViewModel
@Composable
fun LicensesScreenRoot() {
val viewModel = LocalLicensesViewModel.current.viewModel
val onEvent = LicensesViewModel.getEvent()
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.init(context)
onEvent(LicensesEvent.OnInit(context = context))
}
LicensesScreen()
@ -49,7 +50,7 @@ fun LicensesScreenRoot() {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LicensesScreen() {
val state = LocalLicensesViewModel.current.state
val state = LicensesViewModel.getState()
val onNavigate = LocalNavigator.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState(

View file

@ -1,6 +1,11 @@
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
import android.content.Context
import androidx.compose.runtime.Immutable
@Immutable
sealed class LicensesEvent
sealed class LicensesEvent {
data class OnInit(
val context: Context
) : LicensesEvent()
}

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withJson
@ -11,20 +11,34 @@ 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.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import javax.inject.Inject
@HiltViewModel
class LicensesViewModel @Inject constructor(
) : BaseViewModel<LicensesState, LicensesEvent>() {
) : UiViewModel<LicensesState, LicensesEvent>() {
companion object {
@Composable
fun getState() = getState<LicensesViewModel, LicensesState, LicensesEvent>()
@Composable
fun getEvent() = getEvent<LicensesViewModel, LicensesState, LicensesEvent>()
}
private val _state = MutableStateFlow(LicensesState())
override val state = _state.asStateFlow()
override fun onEvent(event: LicensesEvent) {}
override fun onEvent(event: LicensesEvent) {
viewModelScope.launch(Dispatchers.Main) {
when (event) {
is LicensesEvent.OnInit -> init(event)
}
}
}
fun init(context: Context) {
private fun init(event: LicensesEvent.OnInit) {
viewModelScope.launch(Dispatchers.IO) {
if (_state.value.licenses.isNotEmpty()) {
return@launch
@ -32,7 +46,7 @@ class LicensesViewModel @Inject constructor(
val licenses = Libs
.Builder()
.withJson(context, R.raw.aboutlibraries)
.withJson(event.context, R.raw.aboutlibraries)
.build()
.libraries
.toList()

View file

@ -46,7 +46,6 @@ import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomSnackbar
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.screens.book_info.components.BookInfoBackground
@ -61,21 +60,24 @@ import ua.acclorite.book_story.presentation.screens.book_info.components.dialog.
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.BookInfoViewModel
@Composable
fun BookInfoScreenRoot(screen: Screen.BookInfo) {
val viewModel = LocalBookInfoViewModel.current.viewModel
val onEvent = BookInfoViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current
val snackbarState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
viewModel.init(
screen = screen,
snackbarState = snackbarState,
onNavigate = onNavigate,
context = context
onEvent(
BookInfoEvent.OnInit(
screen = screen,
snackbarState = snackbarState,
onNavigate = onNavigate,
context = context
)
)
}
@ -85,7 +87,7 @@ fun BookInfoScreenRoot(screen: Screen.BookInfo) {
DisposableEffect(Unit) {
onDispose {
viewModel.clearViewModel()
onEvent(BookInfoEvent.OnClearViewModel)
}
}
}
@ -94,8 +96,8 @@ fun BookInfoScreenRoot(screen: Screen.BookInfo) {
@Composable
private fun BookInfoScreen(snackbarState: SnackbarHostState) {
val context = LocalContext.current
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val onNavigate = LocalNavigator.current
val listState = rememberLazyListState()

View file

@ -22,17 +22,17 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
/**
* Description section.
*/
@Composable
fun BookInfoDescriptionSection() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val descriptionFocusRequester = remember { FocusRequester() }

View file

@ -36,17 +36,17 @@ 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.core.components.CustomCoverImage
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
/**
* BookInfo's Info section.
*/
@Composable
fun BookInfoInfoSection() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val titleFocusRequester = remember { FocusRequester() }
val authorFocusRequester = remember { FocusRequester() }

View file

@ -11,10 +11,10 @@ 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.Position
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheet
import ua.acclorite.book_story.presentation.core.navigation.NavigationItem
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
/**
* Book Info More Bottom Sheet.
@ -24,7 +24,7 @@ import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
*/
@Composable
fun BookInfoMoreBottomSheet(snackbarState: SnackbarHostState) {
val onEvent = LocalBookInfoViewModel.current.onEvent
val onEvent = BookInfoViewModel.getEvent()
CustomBottomSheet(
modifier = Modifier.fillMaxWidth(),

View file

@ -18,15 +18,15 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.util.calculateProgress
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
/**
* Statistic section.
*/
@Composable
fun BookInfoStatisticSection() {
val state = LocalBookInfoViewModel.current.state
val state = BookInfoViewModel.getState()
val progress by remember {
derivedStateOf {

View file

@ -24,14 +24,14 @@ import ua.acclorite.book_story.presentation.core.components.AnimatedTopAppBarDat
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.NavigationIconButton
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
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.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions
@ -47,10 +47,10 @@ fun BookInfoTopBar(
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 state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -18,24 +18,24 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheet
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
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
/**
* Change cover bottom sheet. Lets user select photo from the gallery and replaces old one.
*/
@Composable
fun BookInfoChangeCoverBottomSheet() {
val state = LocalBookInfoViewModel.current.state
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onEvent = LocalBookInfoViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val context = LocalContext.current
val photoPicker = rememberLauncherForActivityResult(

View file

@ -10,10 +10,10 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheet
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
@ -24,8 +24,8 @@ import java.util.Locale
*/
@Composable
fun BookInfoDetailsBottomSheet() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val context = LocalContext.current
val pattern = remember {

View file

@ -7,13 +7,13 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
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
/**
* Confirm update dialog.
@ -23,9 +23,9 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
*/
@Composable
fun BookInfoConfirmUpdateDialog(snackbarHostState: SnackbarHostState) {
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onEvent = BookInfoViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val context = LocalContext.current
CustomDialogWithContent(

View file

@ -6,27 +6,27 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
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
/**
* Delete dialog. Deletes current book.
*/
@Composable
fun BookInfoDeleteDialog() {
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val onBrowseEvent = LocalBrowseViewModel.current.onEvent
val onEvent = BookInfoViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val onBrowseEvent = BrowseViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -10,16 +10,16 @@ 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.core.components.LocalBookInfoViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.core.components.custom_dialog.SelectableDialogItem
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
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
/**
* Move dialog.
@ -27,10 +27,10 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
*/
@Composable
fun BookInfoMoveDialog() {
val state = LocalBookInfoViewModel.current.state
val onEvent = LocalBookInfoViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = BookInfoViewModel.getState()
val onEvent = BookInfoViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -10,9 +10,19 @@ import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.navigation.Screen
@Immutable
sealed class BookInfoEvent {
data class OnInit(
val screen: Screen.BookInfo,
val snackbarState: SnackbarHostState,
val context: Context,
val onNavigate: OnNavigate
) : BookInfoEvent()
data object OnClearViewModel : BookInfoEvent()
data object OnShowHideChangeCoverBottomSheet : BookInfoEvent()
data object OnShowHideDetailsBottomSheet : BookInfoEvent()

View file

@ -4,12 +4,11 @@ package ua.acclorite.book_story.presentation.screens.book_info.data
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Context.CLIPBOARD_SERVICE
import android.graphics.BitmapFactory
import android.os.Build
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -34,11 +33,10 @@ import ua.acclorite.book_story.domain.use_case.book.UpdateBook
import ua.acclorite.book_story.domain.use_case.book.UpdateBookWithText
import ua.acclorite.book_story.domain.use_case.book.UpdateCoverImageOfBook
import ua.acclorite.book_story.domain.use_case.history.InsertHistory
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import java.util.Date
import javax.inject.Inject
import kotlin.math.roundToInt
@ -55,7 +53,15 @@ class BookInfoViewModel @Inject constructor(
private val canResetCover: CanResetCover,
private val resetCoverImage: ResetCoverImage,
private val checkForTextUpdate: CheckForTextUpdate,
) : BaseViewModel<BookInfoState, BookInfoEvent>() {
) : UiViewModel<BookInfoState, BookInfoEvent>() {
companion object {
@Composable
fun getState() = getState<BookInfoViewModel, BookInfoState, BookInfoEvent>()
@Composable
fun getEvent() = getEvent<BookInfoViewModel, BookInfoState, BookInfoEvent>()
}
private val _state = MutableStateFlow(BookInfoState())
override val state = _state.asStateFlow()
@ -68,6 +74,10 @@ class BookInfoViewModel @Inject constructor(
override fun onEvent(event: BookInfoEvent) {
viewModelScope.launch(eventJob + Dispatchers.Main) {
when (event) {
is BookInfoEvent.OnInit -> init(event)
is BookInfoEvent.OnClearViewModel -> clearViewModel()
is BookInfoEvent.OnShowHideChangeCoverBottomSheet -> {
_state.update {
it.copy(
@ -740,17 +750,12 @@ class BookInfoViewModel @Inject constructor(
}
}
fun init(
screen: Screen.BookInfo,
snackbarState: SnackbarHostState,
context: Context,
onNavigate: OnNavigate
) {
private fun init(event: BookInfoEvent.OnInit) {
viewModelScope.launch(Dispatchers.IO) {
val book = getBookById.execute(screen.bookId)
val book = getBookById.execute(event.screen.bookId)
if (book == null) {
onNavigate {
event.onNavigate {
navigateBack()
}
return@launch
@ -763,17 +768,17 @@ class BookInfoViewModel @Inject constructor(
}
clear()
if (screen.startUpdate) {
if (event.screen.startUpdate) {
onEvent(
BookInfoEvent.OnCheckForTextUpdate(
snackbarState = snackbarState,
context = context
snackbarState = event.snackbarState,
context = event.context
)
)
onNavigate {
event.onNavigate {
putScreen(
Screen.BookInfo(
screen.bookId,
event.screen.bookId,
false
)
)
@ -783,13 +788,7 @@ class BookInfoViewModel @Inject constructor(
}
}
private suspend fun clear() {
eventJob.cancel()
eventJob.join()
eventJob = SupervisorJob()
}
fun clearViewModel() {
private fun clearViewModel() {
viewModelScope.launch(Dispatchers.Main) {
_state.update {
BookInfoState()
@ -800,4 +799,10 @@ class BookInfoViewModel @Inject constructor(
eventJob = SupervisorJob()
}
}
private suspend fun clear() {
eventJob.cancel()
eventJob.join()
eventJob = SupervisorJob()
}
}

View file

@ -24,11 +24,10 @@ 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.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.browse.components.BrowseEmptyPlaceholder
import ua.acclorite.book_story.presentation.screens.browse.components.BrowseStoragePermissionDialog
import ua.acclorite.book_story.presentation.screens.browse.components.adding_dialog.BrowseAddingDialog
@ -36,16 +35,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.BrowseViewModel
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 state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val viewModel = LocalBrowseViewModel.current.viewModel
val state = BrowseViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val permissionState = rememberPermissionState(
permission = Manifest.permission.READ_EXTERNAL_STORAGE
@ -55,7 +54,10 @@ fun BrowseScreenRoot() {
state.value.selectedDirectory
) {
derivedStateOf {
viewModel.filterList(mainState.value)
BrowseViewModel.filterList(
browseState = state.value,
mainState = mainState.value
)
}
}
@ -72,7 +74,7 @@ fun BrowseScreenRoot() {
DisposableEffect(Unit) {
onDispose {
viewModel.clearViewModel()
onEvent(BrowseEvent.OnClearViewModel)
}
}
}
@ -86,9 +88,9 @@ private fun BrowseScreen(
permissionState: PermissionState,
filteredFiles: List<SelectableFile>
) {
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val state = BrowseViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -10,12 +10,12 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.core.components.is_messages.IsError
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.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.ui.Transitions
/**
@ -31,8 +31,8 @@ fun BoxScope.BrowseEmptyPlaceholder(
isFilesEmpty: Boolean,
storagePermissionState: PermissionState
) {
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val state = BrowseViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val onNavigate = LocalNavigator.current
CustomAnimatedVisibility(

View file

@ -9,9 +9,9 @@ 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.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
/**
* Storage permission dialog.
@ -24,7 +24,7 @@ fun BrowseStoragePermissionDialog(
permissionState: PermissionState
) {
val activity = LocalContext.current as ComponentActivity
val onEvent = LocalBrowseViewModel.current.onEvent
val onEvent = BrowseViewModel.getEvent()
CustomDialogWithContent(
title = stringResource(id = R.string.storage_permission),

View file

@ -18,13 +18,13 @@ 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.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.util.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.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import java.util.UUID
/**
@ -34,9 +34,9 @@ import java.util.UUID
@Composable
fun BrowseAddingDialog() {
val context = LocalContext.current
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val state = BrowseViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onNavigate = LocalNavigator.current
CustomDialogWithLazyColumn(

View file

@ -10,9 +10,9 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheet
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.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,8 +23,8 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.compo
*/
@Composable
fun BrowseFilterBottomSheet() {
val state = LocalBrowseViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val state = BrowseViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val pagerState = rememberPagerState(state.value.currentPage) { 3 }

View file

@ -2,7 +2,7 @@ package ua.acclorite.book_story.presentation.screens.browse.components.layout
import androidx.compose.runtime.Composable
import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainViewModel
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.settings.nested.browse.data.BrowseLayout
@ -23,7 +23,7 @@ fun BrowseLayout(
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit
) {
val mainState = LocalMainViewModel.current.state
val mainState = MainViewModel.getState()
when (mainState.value.browseLayout) {
BrowseLayout.LIST -> {

View file

@ -11,9 +11,9 @@ import androidx.compose.runtime.Composable
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.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.header
import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@Composable
@ -25,7 +25,7 @@ fun BrowseGridLayout(
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit,
) {
val state = LocalBrowseViewModel.current.state
val state = BrowseViewModel.getState()
LazyVerticalGrid(
columns = if (autoGridSize) GridCells.Adaptive(170.dp)

View file

@ -9,8 +9,8 @@ import androidx.compose.runtime.Composable
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.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.screens.browse.components.layout.BrowseItem
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@Composable
@ -20,7 +20,7 @@ fun BrowseListLayout(
onFavoriteItemClick: (SelectableFile) -> Unit,
onItemClick: (SelectableFile) -> Unit,
) {
val state = LocalBrowseViewModel.current.state
val state = BrowseViewModel.getState()
LazyColumn(
state = state.value.listState,

View file

@ -29,10 +29,10 @@ import ua.acclorite.book_story.presentation.core.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.core.components.AnimatedTopAppBarData
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.navigation.NavigationIconButton
import ua.acclorite.book_story.presentation.data.MainViewModel
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.settings.nested.browse.data.BrowseFilesStructure
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@ -44,9 +44,9 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BrowseTopBar(filteredFiles: List<SelectableFile>) {
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val state = BrowseViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val focusRequester = remember { FocusRequester() }
val selectedDirectoryName = remember {

View file

@ -27,18 +27,18 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.data.MainViewModel
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.settings.nested.browse.data.BrowseFilesStructure
import java.io.File
@Composable
fun BrowseTopBarDirectoryPath() {
val state = LocalBrowseViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalBrowseViewModel.current.onEvent
val state = BrowseViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = BrowseViewModel.getEvent()
val context = LocalContext.current
val rootDirectory = remember {

View file

@ -16,6 +16,8 @@ import java.io.File
@Immutable
sealed class BrowseEvent {
data object OnClearViewModel : BrowseEvent()
data class OnStoragePermissionRequest(
val activity: ComponentActivity,
val storagePermissionState: PermissionState

View file

@ -9,6 +9,7 @@ import android.os.Environment
import android.provider.Settings
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
@ -29,7 +30,7 @@ import ua.acclorite.book_story.domain.use_case.favorite_directory.UpdateFavorite
import ua.acclorite.book_story.domain.use_case.file_system.GetBookFromFile
import ua.acclorite.book_story.domain.use_case.file_system.GetFilesFromDevice
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.core.util.launchActivity
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
@ -43,7 +44,115 @@ class BrowseViewModel @Inject constructor(
private val getFilesFromDevice: GetFilesFromDevice,
private val insertBook: InsertBook,
private val updateFavoriteDirectory: UpdateFavoriteDirectory,
) : BaseViewModel<BrowseState, BrowseEvent>() {
) : UiViewModel<BrowseState, BrowseEvent>() {
companion object {
@Composable
fun getState() = getState<BrowseViewModel, BrowseState, BrowseEvent>()
@Composable
fun getEvent() = getEvent<BrowseViewModel, BrowseState, BrowseEvent>()
fun filterList(browseState: BrowseState, mainState: MainState): List<SelectableFile> {
fun <T> thenCompareBy(
selector: (T) -> Comparable<*>?
): Comparator<T> {
return if (mainState.browseSortOrderDescending) {
compareByDescending(selector)
} else {
compareBy(selector)
}
}
fun List<SelectableFile>.filterFiles(): List<SelectableFile> {
if (mainState.browseIncludedFilterItems.isEmpty()) {
return this
}
return filter { file ->
when (file.isDirectory) {
true -> {
return@filter this.filter {
if (file == it) {
return@filter false
}
it.fileOrDirectory.path.startsWith(file.fileOrDirectory.path)
}.filterFiles().isNotEmpty()
}
false -> {
return@filter mainState.browseIncludedFilterItems.any {
file.fileOrDirectory.path.endsWith(
it, ignoreCase = true
)
}
}
}
}
}
return browseState.selectableFiles
.filterFiles()
.filter {
if (
browseState.hasSearched
|| mainState.browseFilesStructure == BrowseFilesStructure.ALL_FILES
) {
return@filter !it.isDirectory
}
if (
Environment.getExternalStorageDirectory() == browseState.selectedDirectory
&& it.isFavorite
&& mainState.browsePinFavoriteDirectories
) {
return@filter true
}
it.parentDirectory == browseState.selectedDirectory
}
.sortedWith(
compareByDescending<SelectableFile> {
when (mainState.browsePinFavoriteDirectories) {
true -> it.isFavorite
false -> true
}
}.then(
compareByDescending {
when (mainState.browseSortOrder != BrowseSortOrder.FILE_TYPE) {
true -> it.isDirectory
false -> true
}
}
).then(
thenCompareBy {
when (mainState.browseSortOrder) {
BrowseSortOrder.NAME -> {
it.fileOrDirectory.name.lowercase().trim()
}
BrowseSortOrder.FILE_TYPE -> {
it.isDirectory
}
BrowseSortOrder.FILE_FORMAT -> {
it.fileOrDirectory.extension
}
BrowseSortOrder.FILE_SIZE -> {
it.fileOrDirectory.length()
}
BrowseSortOrder.LAST_MODIFIED -> {
it.fileOrDirectory.lastModified()
}
}
}
)
)
}
}
private val _state = MutableStateFlow(BrowseState())
override val state = _state.asStateFlow()
@ -69,6 +178,8 @@ class BrowseViewModel @Inject constructor(
override fun onEvent(event: BrowseEvent) {
when (event) {
is BrowseEvent.OnClearViewModel -> clearViewModel()
is BrowseEvent.OnStoragePermissionRequest -> {
val legacyStoragePermission = Build.VERSION.SDK_INT < Build.VERSION_CODES.R
@ -605,104 +716,12 @@ class BrowseViewModel @Inject constructor(
}
}
fun filterList(mainState: MainState): List<SelectableFile> {
fun <T> thenCompareBy(
selector: (T) -> Comparable<*>?
): Comparator<T> {
return if (mainState.browseSortOrderDescending) {
compareByDescending(selector)
} else {
compareBy(selector)
private fun clearViewModel() {
viewModelScope.launch(Dispatchers.Main) {
_state.update {
it.copy(isError = false)
}
}
fun List<SelectableFile>.filterFiles(): List<SelectableFile> {
if (mainState.browseIncludedFilterItems.isEmpty()) {
return this
}
return filter { file ->
when (file.isDirectory) {
true -> {
return@filter this.filter {
if (file == it) {
return@filter false
}
it.fileOrDirectory.path.startsWith(file.fileOrDirectory.path)
}.filterFiles().isNotEmpty()
}
false -> {
return@filter mainState.browseIncludedFilterItems.any {
file.fileOrDirectory.path.endsWith(
it, ignoreCase = true
)
}
}
}
}
}
return _state.value.selectableFiles
.filterFiles()
.filter {
if (
_state.value.hasSearched
|| mainState.browseFilesStructure == BrowseFilesStructure.ALL_FILES
) {
return@filter !it.isDirectory
}
if (
Environment.getExternalStorageDirectory() == _state.value.selectedDirectory
&& it.isFavorite
&& mainState.browsePinFavoriteDirectories
) {
return@filter true
}
it.parentDirectory == _state.value.selectedDirectory
}
.sortedWith(
compareByDescending<SelectableFile> {
when (mainState.browsePinFavoriteDirectories) {
true -> it.isFavorite
false -> true
}
}.then(
compareByDescending {
when (mainState.browseSortOrder != BrowseSortOrder.FILE_TYPE) {
true -> it.isDirectory
false -> true
}
}
).then(
thenCompareBy {
when (mainState.browseSortOrder) {
BrowseSortOrder.NAME -> {
it.fileOrDirectory.name.lowercase().trim()
}
BrowseSortOrder.FILE_TYPE -> {
it.isDirectory
}
BrowseSortOrder.FILE_FORMAT -> {
it.fileOrDirectory.extension
}
BrowseSortOrder.FILE_SIZE -> {
it.fileOrDirectory.length()
}
BrowseSortOrder.LAST_MODIFIED -> {
it.fileOrDirectory.lastModified()
}
}
}
)
)
}
private suspend fun getFilesFromDownloads(
@ -721,12 +740,4 @@ class BrowseViewModel @Inject constructor(
}
}
}
fun clearViewModel() {
viewModelScope.launch(Dispatchers.Main) {
_state.update {
it.copy(isError = false)
}
}
}
}

View file

@ -32,26 +32,31 @@ import ua.acclorite.book_story.domain.util.Position
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.GoBackButton
import ua.acclorite.book_story.presentation.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHelpViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.LocalStartViewModel
import ua.acclorite.book_story.presentation.core.components.collapsibleUntilExitScrollBehaviorWithLazyListState
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
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.HelpEvent
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 viewModel = LocalHelpViewModel.current.viewModel
val onEvent = HelpViewModel.getEvent()
LaunchedEffect(Unit) {
viewModel.init(screen = screen)
onEvent(
HelpEvent.OnInit(
screen = screen
)
)
}
HelpScreen()
@ -62,10 +67,10 @@ fun HelpScreenRoot(screen: Screen.Help) {
)
@Composable
private fun HelpScreen() {
val state = LocalHelpViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val onBrowseEvent = LocalBrowseViewModel.current.onEvent
val onStartEvent = LocalStartViewModel.current.onEvent
val state = HelpViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val onBrowseEvent = BrowseViewModel.getEvent()
val onStartEvent = StartViewModel.getEvent()
val onNavigate = LocalNavigator.current
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()

View file

@ -1,6 +1,11 @@
package ua.acclorite.book_story.presentation.screens.help.data
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.presentation.core.navigation.Screen
@Immutable
sealed class HelpEvent
sealed class HelpEvent {
data class OnInit(
val screen: Screen.Help
) : HelpEvent()
}

View file

@ -1,32 +1,40 @@
package ua.acclorite.book_story.presentation.screens.help.data
import androidx.lifecycle.viewModelScope
import androidx.compose.runtime.Composable
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.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import javax.inject.Inject
@HiltViewModel
class HelpViewModel @Inject constructor(
) : BaseViewModel<HelpState, HelpEvent>() {
) : UiViewModel<HelpState, HelpEvent>() {
companion object {
@Composable
fun getState() = getState<HelpViewModel, HelpState, HelpEvent>()
@Composable
fun getEvent() = getEvent<HelpViewModel, HelpState, HelpEvent>()
}
private val _state = MutableStateFlow(HelpState())
override val state = _state.asStateFlow()
override fun onEvent(event: HelpEvent) {}
override fun onEvent(event: HelpEvent) {
when (event) {
is HelpEvent.OnInit -> init(event)
}
}
fun init(screen: Screen.Help) {
viewModelScope.launch {
_state.update {
it.copy(
fromStart = screen.fromStart
)
}
private fun init(event: HelpEvent.OnInit) {
_state.update {
it.copy(
fromStart = event.screen.fromStart
)
}
}
}

View file

@ -43,8 +43,6 @@ import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibi
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.core.components.CustomSnackbar
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.NavigationIconButton
@ -52,13 +50,15 @@ import ua.acclorite.book_story.presentation.core.navigation.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.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 onEvent = LocalHistoryViewModel.current.onEvent
val onEvent = HistoryViewModel.getEvent()
LaunchedEffect(Unit) {
onEvent(HistoryEvent.OnUpdateScrollOffset)
@ -74,9 +74,9 @@ fun HistoryScreenRoot() {
@Composable
private fun HistoryScreen() {
val context = LocalContext.current
val state = LocalHistoryViewModel.current.state
val onEvent = LocalHistoryViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val state = HistoryViewModel.getState()
val onEvent = HistoryViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onNavigate = LocalNavigator.current
val refreshState = rememberPullRefreshState(

View file

@ -6,12 +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.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.core.util.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
/**
* Delete whole history dialog.
@ -19,8 +19,8 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@Composable
fun HistoryDeleteWholeHistoryDialog() {
val context = LocalContext.current
val onEvent = LocalHistoryViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onEvent = HistoryViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
CustomDialogWithContent(
title = stringResource(id = R.string.delete_history),

View file

@ -2,6 +2,7 @@ package ua.acclorite.book_story.presentation.screens.history.data
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -21,7 +22,7 @@ import ua.acclorite.book_story.domain.use_case.history.DeleteWholeHistory
import ua.acclorite.book_story.domain.use_case.history.GetHistory
import ua.acclorite.book_story.domain.use_case.history.InsertHistory
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
@ -35,7 +36,15 @@ class HistoryViewModel @Inject constructor(
private val deleteWholeHistory: DeleteWholeHistory,
private val deleteHistory: DeleteHistory,
private val getBooksById: GetBooksById
) : BaseViewModel<HistoryState, HistoryEvent>() {
) : UiViewModel<HistoryState, HistoryEvent>() {
companion object {
@Composable
fun getState() = getState<HistoryViewModel, HistoryState, HistoryEvent>()
@Composable
fun getEvent() = getEvent<HistoryViewModel, HistoryState, HistoryEvent>()
}
private val _state = MutableStateFlow(HistoryState())
override val state = _state.asStateFlow()
@ -137,7 +146,7 @@ class HistoryViewModel @Inject constructor(
job2?.cancel()
event.snackbarState.currentSnackbarData?.dismiss()
job2 = viewModelScope.launch(Dispatchers.IO) {
job2 = launch(Dispatchers.IO) {
yield()
delay(10000)
yield()

View file

@ -47,25 +47,25 @@ import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.header
import ua.acclorite.book_story.presentation.core.components.is_messages.IsEmpty
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.data.MainViewModel
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.LibraryViewModel
import ua.acclorite.book_story.presentation.ui.DefaultTransition
import ua.acclorite.book_story.presentation.ui.Transitions
@Composable
fun LibraryScreenRoot() {
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val state = LibraryViewModel.getState()
val onEvent = LibraryViewModel.getEvent()
val pagerState = rememberPagerState(
initialPage = state.value.currentPage,
@ -99,9 +99,9 @@ private fun LibraryScreen(
pagerState: PagerState,
refreshState: PullRefreshState
) {
val state = LocalLibraryViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val state = LibraryViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = LibraryViewModel.getEvent()
val onNavigate = LocalNavigator.current
var isScrollInProgress by remember { mutableStateOf(false) }

View file

@ -35,9 +35,9 @@ import ua.acclorite.book_story.presentation.core.components.AnimatedTopAppBar
import ua.acclorite.book_story.presentation.core.components.AnimatedTopAppBarData
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.CustomSearchTextField
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.navigation.NavigationIconButton
import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
/**
* Library Top Bar.
@ -48,8 +48,8 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LibraryTopBar(pagerState: PagerState) {
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val state = LibraryViewModel.getState()
val onEvent = LibraryViewModel.getEvent()
val focusRequester = remember { FocusRequester() }
AnimatedTopAppBar(

View file

@ -6,14 +6,14 @@ 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.core.components.LocalBrowseViewModel
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.core.util.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.data.LibraryEvent
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
/**
* Delete dialog. Deletes all selected books.
@ -21,10 +21,10 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@Composable
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
val state = LibraryViewModel.getState()
val onEvent = LibraryViewModel.getEvent()
val onBrowseEvent = BrowseViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
CustomDialogWithContent(
title = stringResource(id = R.string.delete_books),

View file

@ -9,13 +9,13 @@ 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.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithLazyColumn
import ua.acclorite.book_story.presentation.core.components.custom_dialog.SelectableDialogItem
import ua.acclorite.book_story.presentation.core.util.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
/**
* Move dialog. Moves all selected books to the selected category.
@ -23,9 +23,9 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent
@Composable
fun LibraryMoveDialog(pagerState: PagerState) {
val context = LocalContext.current
val state = LocalLibraryViewModel.current.state
val onEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = LibraryViewModel.getState()
val onEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
CustomDialogWithLazyColumn(
title = stringResource(id = R.string.move_books),

View file

@ -1,5 +1,6 @@
package ua.acclorite.book_story.presentation.screens.library.data
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -18,7 +19,7 @@ import ua.acclorite.book_story.domain.use_case.book.GetBooks
import ua.acclorite.book_story.domain.use_case.book.UpdateBook
import ua.acclorite.book_story.domain.use_case.history.InsertHistory
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import java.util.Date
import javax.inject.Inject
@ -28,7 +29,15 @@ class LibraryViewModel @Inject constructor(
private val updateBook: UpdateBook,
private val deleteBooks: DeleteBooks,
private val insertHistory: InsertHistory
) : BaseViewModel<LibraryState, LibraryEvent>() {
) : UiViewModel<LibraryState, LibraryEvent>() {
companion object {
@Composable
fun getState() = getState<LibraryViewModel, LibraryState, LibraryEvent>()
@Composable
fun getEvent() = getEvent<LibraryViewModel, LibraryState, LibraryEvent>()
}
private val _state = MutableStateFlow(LibraryState())
override val state = _state.asStateFlow()
@ -382,15 +391,3 @@ class LibraryViewModel @Inject constructor(
}
}
}

View file

@ -61,19 +61,17 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomSelectionContainer
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.core.components.is_messages.IsError
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.data.MainViewModel
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.ReaderChapter
import ua.acclorite.book_story.presentation.screens.reader.components.ReaderChaptersDrawer
import ua.acclorite.book_story.presentation.screens.reader.components.ReaderPerceptionExpander
@ -83,16 +81,17 @@ import ua.acclorite.book_story.presentation.screens.reader.components.app_bar.Re
import ua.acclorite.book_story.presentation.screens.reader.components.app_bar.ReaderTopBar
import ua.acclorite.book_story.presentation.screens.reader.components.settings_bottom_sheet.ReaderSettingsBottomSheet
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.ReaderTextAlignment
@Composable
fun ReaderScreenRoot(screen: Screen.Reader) {
val state = LocalReaderViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val viewModel = LocalReaderViewModel.current.viewModel
val state = ReaderViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val context = LocalContext.current as ComponentActivity
val onNavigate = LocalNavigator.current
@ -110,28 +109,34 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
}
LaunchedEffect(Unit) {
viewModel.init(
screen = screen,
onNavigate = onNavigate,
fullscreenMode = mainState.value.fullscreen,
checkForTextUpdate = mainState.value.checkForTextUpdate,
checkForTextUpdateToast = {
if (mainState.value.checkForTextUpdateToast) {
context.getString(R.string.nothing_changed).showToast(
context = context,
longToast = false
)
onEvent(
ReaderEvent.OnInit(
screen = screen,
navigateBack = {
onNavigate {
navigateBack()
}
},
fullscreenMode = mainState.value.fullscreen,
checkForTextUpdate = mainState.value.checkForTextUpdate,
checkForTextUpdateToast = {
if (mainState.value.checkForTextUpdateToast) {
context.getString(R.string.nothing_changed).showToast(
context = context,
longToast = false
)
}
},
activity = context,
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnLoadList)
},
onError = {
it.asString(context)
.showToast(context = context)
}
},
activity = context,
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnLoadList)
},
onError = {
it.asString(context)
.showToast(context = context)
}
)
)
}
LaunchedEffect(canScroll) {
@ -164,10 +169,14 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
}
}
LaunchedEffect(lazyListState) {
viewModel.onUpdateProgress {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}
onEvent(
ReaderEvent.OnUpdateProgress(
refreshList = {
onLibraryEvent(LibraryEvent.OnUpdateBook(it))
onHistoryEvent(HistoryEvent.OnUpdateBook(it))
}
)
)
}
DisposableEffect(mainState.value.screenOrientation) {
context.requestedOrientation = mainState.value.screenOrientation.code
@ -180,7 +189,7 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
DisposableEffect(Unit) {
onDispose {
viewModel.clearViewModel()
onEvent(ReaderEvent.OnClearViewModel)
WindowCompat.getInsetsController(
context.window,
context.window.decorView
@ -194,12 +203,12 @@ fun ReaderScreenRoot(screen: Screen.Reader) {
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
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 state = ReaderViewModel.getState()
val mainState = MainViewModel.getState()
val settingsState = SettingsViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val context = LocalContext.current as ComponentActivity
val onNavigate = LocalNavigator.current

View file

@ -11,16 +11,16 @@ 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.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.components.custom_drawer.CustomModalDrawer
import ua.acclorite.book_story.presentation.core.components.custom_drawer.CustomModalDrawerSelectableItem
import ua.acclorite.book_story.presentation.core.components.custom_drawer.CustomModalDrawerTitleItem
import ua.acclorite.book_story.presentation.core.util.calculateProgress
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.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
/**
* Reader Chapters Drawer.
@ -28,10 +28,10 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
*/
@Composable
fun ReaderChaptersDrawer() {
val state = LocalReaderViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = ReaderViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
CustomModalDrawer(
show = state.value.showChaptersDrawer,

View file

@ -7,8 +7,8 @@ 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.core.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
/**
* Reader Fast Color Preset Change.
@ -24,7 +24,7 @@ fun Modifier.readerFastColorPresetChange(
isLoading: Boolean,
presetChanged: (UIText) -> Unit
): Modifier {
val onSettingsEvent = LocalSettingsViewModel.current.onEvent
val onSettingsEvent = SettingsViewModel.getEvent()
val offset = remember { mutableFloatStateOf(0f) }
return this.then(

View file

@ -26,10 +26,10 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.FontWithName
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.ReaderTextAlignment
/**
@ -65,7 +65,7 @@ fun LazyItemScope.ReaderTextParagraph(
doubleClickTranslationEnabled: Boolean,
toolbarHidden: Boolean
) {
val onEvent = LocalReaderViewModel.current.onEvent
val onEvent = ReaderViewModel.getEvent()
val context = LocalContext.current
Column(

View file

@ -7,10 +7,10 @@ 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.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
/**
* Reader Update Dialog.
@ -18,7 +18,7 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
*/
@Composable
fun ReaderUpdateDialog() {
val onEvent = LocalReaderViewModel.current.onEvent
val onEvent = ReaderViewModel.getEvent()
val onNavigate = LocalNavigator.current
val context = LocalContext.current

View file

@ -31,14 +31,14 @@ import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.Direction
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.util.calculateProgress
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
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.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.ui.Colors
import ua.acclorite.book_story.presentation.ui.HorizontalExpandingTransition
@ -48,10 +48,10 @@ import ua.acclorite.book_story.presentation.ui.HorizontalExpandingTransition
*/
@Composable
fun ReaderBottomBar() {
val state = LocalReaderViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = ReaderViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val bookProgress by remember {
derivedStateOf {
@ -168,10 +168,10 @@ fun ReaderBottomBar() {
*/
@Composable
private fun BottomBarSlider() {
val state = LocalReaderViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = ReaderViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
Slider(
value = state.value.book.progress,

View file

@ -38,18 +38,18 @@ import androidx.compose.ui.unit.sp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.LocalHistoryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalLibraryViewModel
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.navigation.LocalNavigator
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.data.MainViewModel
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.readerFastColorPresetChange
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.ui.Colors
/**
@ -58,11 +58,11 @@ import ua.acclorite.book_story.presentation.ui.Colors
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReaderTopBar() {
val state = LocalReaderViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val onLibraryEvent = LocalLibraryViewModel.current.onEvent
val onHistoryEvent = LocalHistoryViewModel.current.onEvent
val state = ReaderViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val onLibraryEvent = LibraryViewModel.getEvent()
val onHistoryEvent = HistoryViewModel.getEvent()
val context = LocalContext.current as ComponentActivity
val onNavigate = LocalNavigator.current

View file

@ -20,10 +20,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.core.components.CustomLazyColumn
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheet
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.subcategories.ColorsSubcategory
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.MiscSubcategory
@ -39,9 +39,9 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.reader.compo
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReaderSettingsBottomSheet() {
val state = LocalReaderViewModel.current.state
val mainState = LocalMainViewModel.current.state
val onEvent = LocalReaderViewModel.current.onEvent
val state = ReaderViewModel.getState()
val mainState = MainViewModel.getState()
val onEvent = ReaderViewModel.getEvent()
val context = LocalContext.current
val pagerState = rememberPagerState(state.value.currentPage) { 3 }

View file

@ -4,9 +4,9 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalReaderViewModel
import ua.acclorite.book_story.presentation.core.components.custom_bottom_sheet.CustomBottomSheetTabRow
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderViewModel
/**
* Settings bottom sheet tab row.
@ -16,8 +16,7 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
*/
@Composable
fun ReaderSettingsBottomSheetTabRow(pagerState: PagerState) {
val onEvent = LocalReaderViewModel.current.onEvent
val onEvent = ReaderViewModel.getEvent()
val tabItems = listOf(
stringResource(id = R.string.general_tab),
stringResource(id = R.string.reader_tab),

View file

@ -6,9 +6,27 @@ import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.navigation.Screen
@Immutable
sealed class ReaderEvent {
data class OnInit(
val screen: Screen.Reader,
val navigateBack: () -> Unit,
val fullscreenMode: Boolean,
val checkForTextUpdate: Boolean,
val checkForTextUpdateToast: () -> Unit,
val activity: ComponentActivity,
val refreshList: (Book) -> Unit,
val onError: (UIText) -> Unit
) : ReaderEvent()
data class OnUpdateProgress(
val refreshList: (Book) -> Unit
) : ReaderEvent()
data object OnClearViewModel : ReaderEvent()
data object OnTextIsEmpty : ReaderEvent()
data class OnLoadText(

View file

@ -4,6 +4,7 @@ import android.app.SearchManager
import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.snapshotFlow
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@ -25,18 +26,16 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.use_case.book.CheckForTextUpdate
import ua.acclorite.book_story.domain.use_case.book.GetBookById
import ua.acclorite.book_story.domain.use_case.book.GetText
import ua.acclorite.book_story.domain.use_case.book.UpdateBook
import ua.acclorite.book_story.domain.use_case.history.GetLatestHistory
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.core.util.coerceAndPreventNaN
import ua.acclorite.book_story.presentation.core.util.launchActivity
import javax.inject.Inject
@ -50,7 +49,15 @@ class ReaderViewModel @Inject constructor(
private val getLatestHistory: GetLatestHistory,
private val getBookById: GetBookById,
private val checkForTextUpdate: CheckForTextUpdate
) : BaseViewModel<ReaderState, ReaderEvent>() {
) : UiViewModel<ReaderState, ReaderEvent>() {
companion object {
@Composable
fun getState() = getState<ReaderViewModel, ReaderState, ReaderEvent>()
@Composable
fun getEvent() = getEvent<ReaderViewModel, ReaderState, ReaderEvent>()
}
private val _state = MutableStateFlow(ReaderState())
override val state = _state.asStateFlow()
@ -62,6 +69,12 @@ class ReaderViewModel @Inject constructor(
override fun onEvent(event: ReaderEvent) {
viewModelScope.launch(eventJob + Dispatchers.Main) {
when (event) {
is ReaderEvent.OnInit -> init(event)
is ReaderEvent.OnUpdateProgress -> updateProgress(event)
is ReaderEvent.OnClearViewModel -> clearViewModel()
is ReaderEvent.OnTextIsEmpty -> {
_state.update {
it.copy(
@ -505,23 +518,12 @@ class ReaderViewModel @Inject constructor(
}
}
fun init(
screen: Screen.Reader,
onNavigate: OnNavigate,
fullscreenMode: Boolean,
checkForTextUpdate: Boolean,
checkForTextUpdateToast: () -> Unit,
activity: ComponentActivity,
refreshList: (Book) -> Unit,
onError: (UIText) -> Unit
) {
private fun init(event: ReaderEvent.OnInit) {
viewModelScope.launch(Dispatchers.IO) {
val book = getBookById.execute(screen.bookId)
val book = getBookById.execute(event.screen.bookId)
if (book == null) {
onNavigate {
navigateBack()
}
event.navigateBack()
return@launch
}
@ -530,13 +532,13 @@ class ReaderViewModel @Inject constructor(
}
clear()
withContext(Dispatchers.Default) {
withContext(Dispatchers.Main) {
onEvent(
ReaderEvent.OnShowHideMenu(
show = false,
fullscreenMode = fullscreenMode,
fullscreenMode = event.fullscreenMode,
saveCheckpoint = false,
activity = activity
activity = event.activity
)
)
}
@ -544,17 +546,17 @@ class ReaderViewModel @Inject constructor(
onEvent(
ReaderEvent.OnLoadText(
checkForUpdate = {
if (checkForTextUpdate) {
if (event.checkForTextUpdate) {
onEvent(
ReaderEvent.OnCheckTextForUpdate {
checkForTextUpdateToast()
event.checkForTextUpdateToast()
}
)
}
},
refreshList = { refreshList(it) },
refreshList = { event.refreshList(it) },
onError = {
onError(it)
event.onError(it)
},
onTextIsEmpty = {
onEvent(ReaderEvent.OnTextIsEmpty)
@ -565,7 +567,7 @@ class ReaderViewModel @Inject constructor(
}
@OptIn(FlowPreview::class)
fun onUpdateProgress(refreshList: (Book) -> Unit) {
private fun updateProgress(event: ReaderEvent.OnUpdateProgress) {
viewModelScope.launch(Dispatchers.Main) {
snapshotFlow {
_state.value.listState.run { firstVisibleItemIndex to firstVisibleItemScrollOffset }
@ -587,11 +589,23 @@ class ReaderViewModel @Inject constructor(
}
updateBook.execute(_state.value.book)
refreshList(_state.value.book)
event.refreshList(_state.value.book)
}
}
}
private fun clearViewModel() {
viewModelScope.launch(Dispatchers.Main) {
_state.update {
ReaderState()
}
eventJob.cancel()
eventJob.join()
eventJob = SupervisorJob()
}
}
private fun updateChapter(index: Int) {
val (currentChapter, currentChapterProgress) = calculateCurrentChapter(index)
_state.update {
@ -664,16 +678,4 @@ class ReaderViewModel @Inject constructor(
eventJob.join()
eventJob = SupervisorJob()
}
fun clearViewModel() {
viewModelScope.launch(Dispatchers.Main) {
_state.update {
ReaderState()
}
eventJob.cancel()
eventJob.join()
eventJob = SupervisorJob()
}
}
}

View file

@ -3,6 +3,7 @@ package ua.acclorite.book_story.presentation.screens.settings.data
import android.content.Intent
import android.os.Build
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
@ -26,7 +27,7 @@ import ua.acclorite.book_story.domain.use_case.color_preset.UpdateColorPreset
import ua.acclorite.book_story.domain.util.ID
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.util.BaseViewModel
import ua.acclorite.book_story.presentation.core.util.UiViewModel
import ua.acclorite.book_story.presentation.core.util.launchActivity
import javax.inject.Inject
import kotlin.random.Random
@ -39,7 +40,15 @@ class SettingsViewModel @Inject constructor(
private val deleteColorPreset: DeleteColorPreset,
private val selectColorPreset: SelectColorPreset,
private val reorderColorPresets: ReorderColorPresets
) : BaseViewModel<SettingsState, SettingsEvent>() {
) : UiViewModel<SettingsState, SettingsEvent>() {
companion object {
@Composable
fun getState() = getState<SettingsViewModel, SettingsState, SettingsEvent>()
@Composable
fun getEvent() = getEvent<SettingsViewModel, SettingsState, SettingsEvent>()
}
private val _state = MutableStateFlow(SettingsState())
override val state = _state.asStateFlow()
@ -169,8 +178,9 @@ class SettingsViewModel @Inject constructor(
selectedPresetIndex - 1
}
}
val previousColorPreset = colorPresets.getOrNull(previousColorPresetIndex)
?: return@launch
val previousColorPreset =
colorPresets.getOrNull(previousColorPresetIndex)
?: return@launch
yield()
@ -290,11 +300,12 @@ class SettingsViewModel @Inject constructor(
return@launch
}
val nextPosition = if (position == _state.value.colorPresets.lastIndex) {
position - 1
} else {
position
}
val nextPosition =
if (position == _state.value.colorPresets.lastIndex) {
position - 1
} else {
position
}
yield()

View file

@ -4,8 +4,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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.ui.ExpandingTransition
import ua.acclorite.book_story.presentation.ui.isDark
@ -17,8 +17,8 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
*/
@Composable
fun AbsoluteDarkSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val context = LocalContext.current
ExpandingTransition(

View file

@ -63,9 +63,9 @@ import ua.acclorite.book_story.domain.util.Selected
import ua.acclorite.book_story.presentation.core.components.CategoryTitle
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.CustomIconButton
import ua.acclorite.book_story.presentation.core.components.LocalSettingsViewModel
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.SettingsViewModel
import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
import ua.acclorite.book_story.presentation.ui.Transitions
@ -80,8 +80,8 @@ fun ColorPresetSetting(
verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp
) {
val state = LocalSettingsViewModel.current.state
val onEvent = LocalSettingsViewModel.current.onEvent
val state = SettingsViewModel.getState()
val onEvent = SettingsViewModel.getEvent()
val context = LocalContext.current
val reorderableListState = rememberReorderableLazyListState(

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable
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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.ui.DarkTheme
@ -16,8 +16,8 @@ import ua.acclorite.book_story.presentation.ui.DarkTheme
*/
@Composable
fun DarkThemeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SegmentedButtonWithTitle(
title = stringResource(id = R.string.dark_theme_option),

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun FastColorPresetChangeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.fastColorPresetChange,

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable
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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
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,8 +18,8 @@ import ua.acclorite.book_story.presentation.ui.isDark
*/
@Composable
fun PureDarkSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ExpandingTransition(visible = state.value.darkTheme.isDark()) {
SegmentedButtonWithTitle(

View file

@ -9,8 +9,8 @@ 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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
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,8 +25,8 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
*/
@Composable
fun ThemeContrastSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val themeContrastTheme = remember { mutableStateOf(state.value.theme) }

View file

@ -41,9 +41,9 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.components.CategoryTitle
import ua.acclorite.book_story.presentation.core.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.ui.Theme
import ua.acclorite.book_story.presentation.ui.ThemeContrast
import ua.acclorite.book_story.presentation.ui.animatedColorScheme
@ -59,8 +59,8 @@ fun ThemeSetting(
verticalPadding: Dp = 8.dp,
horizontalPadding: Dp = 18.dp
) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val themes = remember {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Constants.THEMES

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable
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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseFilesStructure
@ -16,8 +16,8 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
*/
@Composable
fun BrowseFilesStructureSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SegmentedButtonWithTitle(
title = stringResource(id = R.string.browse_files_structure_option),

View file

@ -18,9 +18,9 @@ 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.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
/**
* Browse Filter setting.
@ -28,8 +28,8 @@ import ua.acclorite.book_story.presentation.data.MainEvent
*/
fun LazyListScope.BrowseFilterSetting() {
items(Constants.EXTENSIONS, key = { it }) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
FilterItem(
item = it,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.browse.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
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,8 +15,8 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
*/
@Composable
fun BrowseGridSizeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ExpandingTransition(visible = state.value.browseLayout == BrowseLayout.GRID) {
SliderWithTitle(

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable
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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseLayout
@ -16,8 +16,8 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
*/
@Composable
fun BrowseLayoutSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SegmentedButtonWithTitle(
title = stringResource(id = R.string.browse_layout_option),

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.browse.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
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,8 +15,8 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
*/
@Composable
fun BrowsePinFavoriteDirectoriesSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ExpandingTransition(visible = state.value.browseFilesStructure == BrowseFilesStructure.DIRECTORIES) {
SwitchWithTitle(

View file

@ -25,8 +25,8 @@ 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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.BrowseSortOrder
/**
@ -35,8 +35,8 @@ import ua.acclorite.book_story.presentation.screens.settings.nested.browse.data.
*/
fun LazyListScope.BrowseSortOrderSetting() {
items(BrowseSortOrder.entries, key = { it.name }) {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SortItem(
item = it,

View file

@ -5,9 +5,9 @@ import androidx.compose.runtime.Composable
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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
/**
@ -16,8 +16,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
*/
@Composable
fun AppLanguageSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ChipsWithTitle(
title = stringResource(id = R.string.language_option),

View file

@ -13,12 +13,12 @@ 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.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.components.LocalSettingsViewModel
import ua.acclorite.book_story.presentation.core.components.custom_dialog.CustomDialogWithContent
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
/**
* Check For Updates setting.
@ -28,9 +28,9 @@ import ua.acclorite.book_story.presentation.screens.settings.data.SettingsEvent
@SuppressLint("InlinedApi")
@Composable
fun CheckForUpdatesSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val onSettingsEvent = LocalSettingsViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val onSettingsEvent = SettingsViewModel.getEvent()
val activity = LocalContext.current as ComponentActivity
val notificationsPermissionState = rememberPermissionState(

View file

@ -4,8 +4,8 @@ import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -15,8 +15,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
@SuppressLint("InlinedApi")
@Composable
fun DoublePressExitSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.doublePressExit,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun CheckForTextUpdateSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.checkForTextUpdate,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.ui.ExpandingTransition
@ -14,8 +14,8 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
*/
@Composable
fun CheckForTextUpdateToastSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ExpandingTransition(visible = state.value.checkForTextUpdate) {
SwitchWithTitle(

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun CutoutPaddingSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.cutoutPadding,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun DoubleClickTranslationSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.doubleClickTranslation,

View file

@ -6,9 +6,9 @@ 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.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
/**
@ -17,8 +17,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWit
*/
@Composable
fun FontFamilySetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find {

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun FontSizeSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.fontSize to "pt",

View file

@ -7,9 +7,9 @@ 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.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
/**
@ -18,8 +18,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.Segmente
*/
@Composable
fun FontStyleSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
val fontFamily = remember(state.value.fontFamily) {
Constants.FONTS.find {

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun FullscreenSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.fullscreen,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun HideBarsOnFastScrollSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.hideBarsOnFastScroll,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun KeepScreenOnSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.keepScreenOn,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun LetterSpacingSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.letterSpacing to "pt",

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun LineHeightSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.lineHeight to "pt",

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun ParagraphHeightSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.paragraphHeight to "pt",

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.data.ReaderTextAlignment
import ua.acclorite.book_story.presentation.ui.ExpandingTransition
@ -15,8 +15,8 @@ import ua.acclorite.book_story.presentation.ui.ExpandingTransition
*/
@Composable
fun ParagraphIndentationSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
ExpandingTransition(
visible = state.value.textAlignment != ReaderTextAlignment.CENTER &&

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun PerceptionExpanderPaddingSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.perceptionExpanderPadding to "pt",

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWi
*/
@Composable
fun PerceptionExpanderSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SwitchWithTitle(
selected = state.value.perceptionExpander,

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.presentation.screens.settings.nested.reader.comp
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.settings.components.SliderWithTitle
/**
@ -13,8 +13,8 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
*/
@Composable
fun PerceptionExpanderThicknessSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val state = MainViewModel.getState()
val onMainEvent = MainViewModel.getEvent()
SliderWithTitle(
value = state.value.perceptionExpanderThickness to "pt",

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