refactor: about viewmodel

This commit is contained in:
Acclorite 2025-08-07 23:57:01 +03:00
parent 3f0cd812a5
commit eb87ef8b45
No known key found for this signature in database
GPG key ID: 6E54C611F6EE8593
19 changed files with 152 additions and 94 deletions

View file

@ -0,0 +1,22 @@
/*
* Book's Story free and open-source Material You eBook reader.
* Copyright (C) 2024-2025 Acclorite
* SPDX-License-Identifier: GPL-3.0-only
*/
package ua.acclorite.book_story.presentation.about
import androidx.compose.runtime.Immutable
@Immutable
sealed class AboutEffect {
data class OnNavigateToBrowserPage(
val page: String
) : AboutEffect()
data object OnNavigateToLicenses : AboutEffect()
data object OnNavigateToCredits : AboutEffect()
data object OnNavigateBack : AboutEffect()
}

View file

@ -6,13 +6,17 @@
package ua.acclorite.book_story.presentation.about
import android.content.Context
import androidx.compose.runtime.Immutable
@Immutable
sealed class AboutEvent {
data class OnNavigateToBrowserPage(
val page: String,
val context: Context
val page: String
) : AboutEvent()
data object OnNavigateToLicenses : AboutEvent()
data object OnNavigateToCredits : AboutEvent()
data object OnNavigateBack : AboutEvent()
}

View file

@ -6,38 +6,37 @@
package ua.acclorite.book_story.presentation.about
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ua.acclorite.book_story.R
import ua.acclorite.book_story.ui.common.helpers.launchActivity
import ua.acclorite.book_story.ui.common.helpers.showToast
import javax.inject.Inject
@HiltViewModel
class AboutModel @Inject constructor() : ViewModel() {
private val _effects = MutableSharedFlow<AboutEffect>()
val effects = _effects.asSharedFlow()
fun onEvent(event: AboutEvent) {
viewModelScope.launch {
when (event) {
is AboutEvent.OnNavigateToBrowserPage -> {
viewModelScope.launch {
val intent = Intent(
Intent.ACTION_VIEW,
event.page.toUri()
)
_effects.emit(AboutEffect.OnNavigateToBrowserPage(event.page))
}
intent.launchActivity(event.context as ComponentActivity) {
withContext(Dispatchers.Main) {
event.context.getString(R.string.error_no_browser)
.showToast(context = event.context, longToast = false)
is AboutEvent.OnNavigateToLicenses -> {
_effects.emit(AboutEffect.OnNavigateToLicenses)
}
is AboutEvent.OnNavigateToCredits -> {
_effects.emit(AboutEffect.OnNavigateToCredits)
}
is AboutEvent.OnNavigateBack -> {
_effects.emit(AboutEffect.OnNavigateBack)
}
}
}

View file

@ -12,12 +12,10 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.parcelize.Parcelize
import ua.acclorite.book_story.presentation.credits.CreditsScreen
import ua.acclorite.book_story.presentation.licenses.LicensesScreen
import ua.acclorite.book_story.presentation.navigator.Screen
import ua.acclorite.book_story.ui.about.AboutContent
import ua.acclorite.book_story.ui.about.AboutEffects
import ua.acclorite.book_story.ui.common.components.top_bar.collapsibleTopAppBarScrollBehavior
import ua.acclorite.book_story.ui.navigator.LocalNavigator
@Parcelize
object AboutScreen : Screen, Parcelable {
@ -25,24 +23,19 @@ object AboutScreen : Screen, Parcelable {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.current
val screenModel = hiltViewModel<AboutModel>()
val (scrollBehavior, listState) = TopAppBarDefaults.collapsibleTopAppBarScrollBehavior()
AboutEffects(screenModel.effects)
AboutContent(
scrollBehavior = scrollBehavior,
listState = listState,
navigateToBrowserPage = screenModel::onEvent,
navigateToLicenses = {
navigator.push(LicensesScreen)
},
navigateToCredits = {
navigator.push(CreditsScreen)
},
navigateBack = {
navigator.pop()
}
navigateToLicenses = screenModel::onEvent,
navigateToCredits = screenModel::onEvent,
navigateBack = screenModel::onEvent
)
}
}

View file

@ -14,9 +14,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.parcelize.Parcelize
import ua.acclorite.book_story.presentation.about.AboutModel
import ua.acclorite.book_story.presentation.navigator.Screen
import ua.acclorite.book_story.ui.about.AboutEffects
import ua.acclorite.book_story.ui.common.components.top_bar.collapsibleTopAppBarScrollBehavior
import ua.acclorite.book_story.ui.credits.CreditsContent
import ua.acclorite.book_story.ui.navigator.LocalNavigator
@Parcelize
object CreditsScreen : Screen, Parcelable {
@ -24,18 +24,17 @@ object CreditsScreen : Screen, Parcelable {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.current
val screenModel = hiltViewModel<AboutModel>()
val (scrollBehavior, listState) = TopAppBarDefaults.collapsibleTopAppBarScrollBehavior()
AboutEffects(screenModel.effects)
CreditsContent(
scrollBehavior = scrollBehavior,
listState = listState,
navigateToBrowserPage = screenModel::onEvent,
navigateBack = {
navigator.pop()
}
navigateBack = screenModel::onEvent
)
}
}

View file

@ -20,9 +20,9 @@ import kotlinx.parcelize.Parcelize
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.about.AboutModel
import ua.acclorite.book_story.presentation.navigator.Screen
import ua.acclorite.book_story.ui.about.AboutEffects
import ua.acclorite.book_story.ui.common.components.top_bar.collapsibleTopAppBarScrollBehavior
import ua.acclorite.book_story.ui.license_info.LicenseInfoContent
import ua.acclorite.book_story.ui.navigator.LocalNavigator
@Parcelize
data class LicenseInfoScreen(val uniqueId: String) : Screen, Parcelable {
@ -30,7 +30,6 @@ data class LicenseInfoScreen(val uniqueId: String) : Screen, Parcelable {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.current
val screenModel = hiltViewModel<AboutModel>()
val context = LocalContext.current
@ -42,14 +41,14 @@ data class LicenseInfoScreen(val uniqueId: String) : Screen, Parcelable {
}
}
AboutEffects(screenModel.effects)
LicenseInfoContent(
library = library.value,
scrollBehavior = scrollBehavior,
listState = listState,
navigateToBrowserPage = screenModel::onEvent,
navigateBack = {
navigator.pop()
}
navigateBack = screenModel::onEvent
)
}
}

View file

@ -52,8 +52,7 @@ fun AboutBadges(
badge.url?.let {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = it,
context = context
page = it
)
)
}

View file

@ -18,9 +18,9 @@ fun AboutContent(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit,
navigateBack: () -> Unit
navigateToLicenses: (AboutEvent.OnNavigateToLicenses) -> Unit,
navigateToCredits: (AboutEvent.OnNavigateToCredits) -> Unit,
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
AboutScaffold(
scrollBehavior = scrollBehavior,

View file

@ -0,0 +1,57 @@
/*
* Book's Story free and open-source Material You eBook reader.
* Copyright (C) 2024-2025 Acclorite
* SPDX-License-Identifier: GPL-3.0-only
*/
package ua.acclorite.book_story.ui.about
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.core.net.toUri
import kotlinx.coroutines.flow.SharedFlow
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.about.AboutEffect
import ua.acclorite.book_story.presentation.credits.CreditsScreen
import ua.acclorite.book_story.presentation.licenses.LicensesScreen
import ua.acclorite.book_story.ui.common.helpers.LocalActivity
import ua.acclorite.book_story.ui.common.helpers.launchActivity
import ua.acclorite.book_story.ui.common.helpers.showToast
import ua.acclorite.book_story.ui.navigator.LocalNavigator
@Composable
fun AboutEffects(effects: SharedFlow<AboutEffect>) {
val navigator = LocalNavigator.current
val activity = LocalActivity.current
LaunchedEffect(Unit) {
effects.collect { effect ->
when (effect) {
is AboutEffect.OnNavigateToBrowserPage -> {
val intent = Intent(
Intent.ACTION_VIEW,
effect.page.toUri()
)
intent.launchActivity(activity) {
activity.getString(R.string.error_no_browser)
.showToast(context = activity, longToast = false)
}
}
is AboutEffect.OnNavigateToLicenses -> {
navigator.push(LicensesScreen)
}
is AboutEffect.OnNavigateToCredits -> {
navigator.push(CreditsScreen)
}
is AboutEffect.OnNavigateBack -> {
navigator.pop()
}
}
}
}
}

View file

@ -21,7 +21,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -36,11 +35,9 @@ fun AboutLayout(
paddingValues: PaddingValues,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit
navigateToLicenses: (AboutEvent.OnNavigateToLicenses) -> Unit,
navigateToCredits: (AboutEvent.OnNavigateToCredits) -> Unit
) {
val context = LocalContext.current
LazyColumnWithScrollbar(
Modifier
.fillMaxSize()
@ -82,8 +79,7 @@ fun AboutLayout(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = LinkData.RELEASES,
context = context
page = LinkData.RELEASES
)
)
}
@ -96,8 +92,7 @@ fun AboutLayout(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = LinkData.ISSUES,
context = context
page = LinkData.ISSUES
)
)
}
@ -111,8 +106,7 @@ fun AboutLayout(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = LinkData.CONTRIBUTORS,
context = context
page = LinkData.CONTRIBUTORS
)
)
}
@ -123,7 +117,7 @@ fun AboutLayout(
title = stringResource(id = R.string.licenses_option),
description = null
) {
navigateToLicenses()
navigateToLicenses(AboutEvent.OnNavigateToLicenses)
}
}
@ -132,7 +126,7 @@ fun AboutLayout(
title = stringResource(id = R.string.credits_option),
description = null
) {
navigateToCredits()
navigateToCredits(AboutEvent.OnNavigateToCredits)
}
}
@ -143,8 +137,7 @@ fun AboutLayout(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = LinkData.TRANSLATION,
context = context
page = LinkData.TRANSLATION
)
)
}
@ -157,8 +150,7 @@ fun AboutLayout(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = LinkData.SUPPORT,
context = context
page = LinkData.SUPPORT
)
)
}

View file

@ -26,9 +26,9 @@ fun AboutScaffold(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit,
navigateBack: () -> Unit
navigateToLicenses: (AboutEvent.OnNavigateToLicenses) -> Unit,
navigateToCredits: (AboutEvent.OnNavigateToCredits) -> Unit,
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
Scaffold(
Modifier

View file

@ -14,6 +14,7 @@ import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.about.AboutEvent
import ua.acclorite.book_story.ui.common.components.common.StyledText
import ua.acclorite.book_story.ui.navigator.NavigatorBackIconButton
@ -21,14 +22,14 @@ import ua.acclorite.book_story.ui.navigator.NavigatorBackIconButton
@Composable
fun AboutTopBar(
scrollBehavior: TopAppBarScrollBehavior,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
LargeTopAppBar(
title = {
StyledText(stringResource(id = R.string.about_screen))
},
navigationIcon = {
NavigatorBackIconButton(navigateBack = navigateBack)
NavigatorBackIconButton(navigateBack = { navigateBack(AboutEvent.OnNavigateBack) })
},
scrollBehavior = scrollBehavior,
colors = TopAppBarDefaults.topAppBarColors(

View file

@ -18,7 +18,7 @@ fun CreditsContent(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
CreditsScaffold(
scrollBehavior = scrollBehavior,

View file

@ -13,7 +13,6 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import ua.acclorite.book_story.presentation.about.AboutEvent
import ua.acclorite.book_story.ui.common.components.common.LazyColumnWithScrollbar
import ua.acclorite.book_story.ui.credits.data.CreditData
@ -24,7 +23,6 @@ fun CreditsLayout(
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit
) {
val context = LocalContext.current
LazyColumnWithScrollbar(
Modifier
.fillMaxSize()
@ -39,8 +37,7 @@ fun CreditsLayout(
credit.website?.let { website ->
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = website,
context = context
page = website
)
)
}

View file

@ -26,7 +26,7 @@ fun CreditsScaffold(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
Scaffold(
Modifier

View file

@ -14,6 +14,7 @@ import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.about.AboutEvent
import ua.acclorite.book_story.ui.common.components.common.StyledText
import ua.acclorite.book_story.ui.navigator.NavigatorBackIconButton
@ -21,14 +22,14 @@ import ua.acclorite.book_story.ui.navigator.NavigatorBackIconButton
@Composable
fun CreditsTopBar(
scrollBehavior: TopAppBarScrollBehavior,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
LargeTopAppBar(
title = {
StyledText(stringResource(id = R.string.credits_option))
},
navigationIcon = {
NavigatorBackIconButton(navigateBack = navigateBack)
NavigatorBackIconButton(navigateBack = { navigateBack(AboutEvent.OnNavigateBack) })
},
scrollBehavior = scrollBehavior,
colors = TopAppBarDefaults.topAppBarColors(

View file

@ -20,7 +20,7 @@ fun LicenseInfoContent(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
LicenseInfoScaffold(
library = library,

View file

@ -29,7 +29,7 @@ fun LicenseInfoScaffold(
scrollBehavior: TopAppBarScrollBehavior,
listState: LazyListState,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
Scaffold(
Modifier

View file

@ -14,7 +14,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.mikepenz.aboutlibraries.entity.Library
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.about.AboutEvent
@ -28,19 +27,16 @@ fun LicenseInfoTopBar(
library: Library,
scrollBehavior: TopAppBarScrollBehavior,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateBack: () -> Unit
navigateBack: (AboutEvent.OnNavigateBack) -> Unit
) {
val context = LocalContext.current
LargeTopAppBar(
title = {
StyledText(
text = library.name,
maxLines = 1
text = library.name
)
},
navigationIcon = {
NavigatorBackIconButton(navigateBack = navigateBack)
NavigatorBackIconButton(navigateBack = { navigateBack(AboutEvent.OnNavigateBack) })
},
actions = {
if (library.website?.isNotBlank() == true) {
@ -51,8 +47,7 @@ fun LicenseInfoTopBar(
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = library.website!!,
context = context
page = library.website!!
)
)
}