refactor(settings): route settings UI through intents and navigation effects

This commit is contained in:
2026-05-30 19:16:38 +03:00
parent 10453287a7
commit 2daab8d0f7
11 changed files with 231 additions and 153 deletions
@@ -59,6 +59,7 @@ import dev.meloda.fast.model.api.domain.VkUser
import dev.meloda.fast.navigation.Main
import dev.meloda.fast.navigation.mainScreen
import dev.meloda.fast.photoviewer.presentation.PhotoViewDialog
import dev.meloda.fast.settings.model.SettingsNavigationIntent
import dev.meloda.fast.settings.navigation.navigateToSettings
import dev.meloda.fast.settings.navigation.settingsScreen
import dev.meloda.fast.ui.R
@@ -362,18 +363,31 @@ fun RootScreen(
)
settingsScreen(
onBack = navController::navigateUp,
onLogOutButtonClicked = { navController.navigateToAuth(true) },
onLanguageItemClicked = navController::navigateToLanguagePicker,
onRestartRequired = {
handleNavigationIntent = { intent ->
when (intent) {
SettingsNavigationIntent.Back -> navController.navigateUp()
SettingsNavigationIntent.Language -> navController.navigateToLanguagePicker()
SettingsNavigationIntent.Restart -> {
activity?.let {
val intent = Intent(activity, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
activity.startActivity(intent)
val intent =
Intent(activity, MainActivity::class.java)
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP
)
activity.finish()
activity.startActivity(intent)
}
}
SettingsNavigationIntent.LogOut -> {
navController.navigateToAuth(true)
}
}
}
)
languagePickerScreen(onBack = navController::navigateUp)
}
@@ -641,7 +641,7 @@ class LongPollUpdatesParser(
).listenValue(this) { state ->
state.processState(
error = { error ->
logger.error(this::class, "loadMessage(): ERROR: $error")
logger.error(this@LongPollUpdatesParser::class, "loadMessage(): ERROR: $error")
continuation.resume(null)
},
success = { response ->
@@ -670,7 +670,7 @@ class LongPollUpdatesParser(
).listenValue(coroutineScope) { state ->
state.processState(
error = { error ->
logger.error(this::class, "loadConvo(): ERROR: $error")
logger.error(this@LongPollUpdatesParser::class, "loadConvo(): ERROR: $error")
continuation.resume(null)
},
success = { response ->
@@ -11,9 +11,10 @@ import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.common.extensions.findWithIndex
import dev.meloda.fast.common.extensions.listenValue
import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.common.extensions.updateValue
import dev.meloda.fast.common.model.DarkMode
import dev.meloda.fast.common.model.NetworkLogLevel
import dev.meloda.fast.common.model.LongPollState
import dev.meloda.fast.common.model.NetworkLogLevel
import dev.meloda.fast.common.model.UiText
import dev.meloda.fast.common.model.parseString
import dev.meloda.fast.data.UserConfig
@@ -24,10 +25,13 @@ import dev.meloda.fast.datastore.SettingsKeys
import dev.meloda.fast.datastore.UserSettings
import dev.meloda.fast.domain.GetCurrentAccountUseCase
import dev.meloda.fast.domain.LoadUserByIdUseCase
import dev.meloda.fast.logger.FastLogger
import dev.meloda.fast.model.database.AccountEntity
import dev.meloda.fast.settings.model.HapticType
import dev.meloda.fast.settings.model.SettingsDialog
import dev.meloda.fast.settings.model.SettingsIntent
import dev.meloda.fast.settings.model.SettingsItem
import dev.meloda.fast.settings.model.SettingsNavigationIntent
import dev.meloda.fast.settings.model.SettingsScreenState
import dev.meloda.fast.settings.model.TextProvider
import dev.meloda.fast.ui.R
@@ -45,35 +49,83 @@ class SettingsViewModel(
private val getCurrentAccountUseCase: GetCurrentAccountUseCase,
private val userSettings: UserSettings,
private val resources: Resources,
private val longPollController: LongPollController
private val longPollController: LongPollController,
private val logger: FastLogger
) : ViewModel() {
private val _screenState = MutableStateFlow(SettingsScreenState.EMPTY)
val screenState = _screenState.asStateFlow()
private val screenState = MutableStateFlow(SettingsScreenState.EMPTY)
val screenStateFlow get() = screenState.asStateFlow()
private val _hapticType = MutableStateFlow<HapticType?>(null)
val hapticType = _hapticType.asStateFlow()
private val hapticType = MutableStateFlow<HapticType?>(null)
val hapticTypeFlow get() = hapticType.asStateFlow()
private val _dialog = MutableStateFlow<SettingsDialog?>(null)
val dialog = _dialog.asStateFlow()
private val navigationIntent = MutableStateFlow<SettingsNavigationIntent?>(null)
val navigationIntentFlow get() = navigationIntent.asStateFlow()
private val _isNeedToRestart = MutableStateFlow(false)
val isNeedToRestart = _isNeedToRestart.asStateFlow()
private val settings = MutableStateFlow<List<SettingsItem<*>>>(emptyList())
private val settings = mutableListOf<SettingsItem<*>>()
private var showDebugCategory: Boolean = userSettings.showDebugCategory.value
init {
createSettings()
}
fun onDialogConfirmed(dialog: SettingsDialog, bundle: Bundle) {
onDialogDismissed(dialog)
fun handleIntent(intent: SettingsIntent) {
when (intent) {
SettingsIntent.BackClick -> {
navigationIntent.setValue { SettingsNavigationIntent.Back }
}
SettingsIntent.ConsumePerformHaptic -> {
hapticType.setValue { null }
}
is SettingsIntent.ItemClick -> {
onSettingsItemClicked(intent.key)
}
is SettingsIntent.ItemLongClick -> {
onSettingsItemLongClicked(intent.key)
}
is SettingsIntent.ItemValueChanged -> {
onSettingsItemChanged(intent.key, intent.newValue)
}
is SettingsIntent.Dialog -> {
when (intent) {
SettingsIntent.Dialog.CancelClick -> Unit
is SettingsIntent.Dialog.ConfirmClick -> {
onDialogConfirmed(intent.bundle)
}
SettingsIntent.Dialog.Dismiss -> {
onDialogDismissed()
}
is SettingsIntent.Dialog.ItemPick -> {
onDialogItemPicked(intent.bundle)
}
}
}
}
}
private fun setDialog(dialog: SettingsDialog?) {
screenState.updateValue { copy(dialog = dialog) }
}
private fun onDialogConfirmed(bundle: Bundle?) {
val dialog = screenState.value.dialog ?: return
onDialogDismissed()
when (dialog) {
is SettingsDialog.LogOut -> onLogOutAlertPositiveClick()
is SettingsDialog.PerformCrash -> onPerformCrashPositiveButtonClicked()
is SettingsDialog.ImportAuthData -> {
if (bundle == null) return
val accessToken = bundle.getString("ACCESS_TOKEN") ?: return
val exchangeToken = bundle.getString("EXCHANGE_TOKEN")
val trustedHash = bundle.getString("TRUSTED_HASH")
@@ -89,6 +141,10 @@ class SettingsViewModel(
).listenValue(viewModelScope) { state ->
state.processState(
error = { error ->
logger.error(
this@SettingsViewModel::class,
"importAuthInfo(): loadUserById(): ERROR: $error"
)
UserConfig.accessToken = oldToken
},
success = { user ->
@@ -113,7 +169,7 @@ class SettingsViewModel(
accountsRepository.storeAccounts(listOf(account))
_isNeedToRestart.setValue { true }
navigationIntent.setValue { SettingsNavigationIntent.Restart }
}
)
}
@@ -124,7 +180,8 @@ class SettingsViewModel(
}
}
fun onDialogDismissed(dialog: SettingsDialog) {
private fun onDialogDismissed() {
val dialog = screenState.value.dialog ?: return
when (dialog) {
is SettingsDialog.LogOut -> Unit
is SettingsDialog.PerformCrash -> Unit
@@ -132,10 +189,11 @@ class SettingsViewModel(
is SettingsDialog.ExportAuthData -> Unit
}
_dialog.setValue { null }
setDialog(null)
}
fun onDialogItemPicked(dialog: SettingsDialog, bundle: Bundle) {
private fun onDialogItemPicked(bundle: Bundle?) {
val dialog = screenState.value.dialog ?: return
when (dialog) {
is SettingsDialog.LogOut -> Unit
is SettingsDialog.PerformCrash -> Unit
@@ -144,7 +202,7 @@ class SettingsViewModel(
}
}
fun onLogOutAlertPositiveClick() {
private fun onLogOutAlertPositiveClick() {
viewModelScope.launch(Dispatchers.IO) {
val tasks = listOf(
async {
@@ -164,35 +222,37 @@ class SettingsViewModel(
)
tasks.awaitAll()
navigationIntent.setValue { SettingsNavigationIntent.LogOut }
}
}
fun onPerformCrashPositiveButtonClicked() {
private fun onPerformCrashPositiveButtonClicked() {
throw Exception("Test exception")
}
fun onSettingsItemClicked(key: String) {
private fun onSettingsItemClicked(key: String) {
when (key) {
SettingsKeys.KEY_ACCOUNT_LOGOUT -> {
_dialog.setValue { SettingsDialog.LogOut }
setDialog(SettingsDialog.LogOut)
}
SettingsKeys.KEY_DEBUG_PERFORM_CRASH -> {
_dialog.setValue { SettingsDialog.PerformCrash }
setDialog(SettingsDialog.PerformCrash)
}
SettingsKeys.KEY_DEBUG_IMPORT_AUTH_DATA -> {
_dialog.setValue { SettingsDialog.ImportAuthData }
setDialog(SettingsDialog.ImportAuthData)
}
SettingsKeys.KEY_DEBUG_EXPORT_AUTH_DATA -> {
_dialog.setValue {
setDialog(
SettingsDialog.ExportAuthData(
accessToken = UserConfig.accessToken,
exchangeToken = UserConfig.exchangeToken,
trustedHash = UserConfig.trustedHash
)
}
)
}
SettingsKeys.KEY_DEBUG_HIDE_DEBUG_LIST -> {
@@ -203,13 +263,13 @@ class SettingsViewModel(
createSettings()
_hapticType.update { HapticType.REJECT }
_screenState.setValue { old -> old.copy(showDebugOptions = false) }
hapticType.update { HapticType.REJECT }
showDebugCategory = false
}
}
}
fun onSettingsItemLongClicked(key: String) {
private fun onSettingsItemLongClicked(key: String) {
when (key) {
SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS -> {
if (AppSettings.Debug.showDebugCategory) return
@@ -219,18 +279,18 @@ class SettingsViewModel(
createSettings()
_hapticType.update { HapticType.LONG_PRESS }
_screenState.setValue { old -> old.copy(showDebugOptions = true) }
hapticType.update { HapticType.LONG_PRESS }
showDebugCategory = true
}
}
}
fun onSettingsItemChanged(key: String, newValue: Any?) {
settings.value.findWithIndex { it.key == key }?.let { (index, item) ->
private fun onSettingsItemChanged(key: String, newValue: Any?) {
settings.findWithIndex { it.key == key }?.let { (index, item) ->
item.updateValue(newValue)
item.updateText()
_screenState.setValue { old ->
screenState.setValue { old ->
old.copy(
settings = old.settings.toMutableList().apply {
this[index] = item.asPresentation(resources)
@@ -311,10 +371,6 @@ class SettingsViewModel(
}
}
fun onHapticPerformed() {
_hapticType.update { null }
}
private fun createSettings() {
val accountVisible = UserConfig.isLoggedIn()
val accountTitle = SettingsItem.Title(
@@ -512,7 +568,8 @@ class SettingsViewModel(
values = logLevelValues.keys.toList().map(NetworkLogLevel::value)
).apply {
textProvider = TextProvider { item ->
val textValue = logLevelValues[NetworkLogLevel.parse(item.value)].parseString(resources)
val textValue =
logLevelValues[NetworkLogLevel.parse(item.value)].parseString(resources)
UiText.Simple("Current value: $textValue")
}
@@ -602,12 +659,10 @@ class SettingsViewModel(
}
private fun emitSettings(newSettings: List<SettingsItem<*>>) {
settings.update { newSettings }
settings.clear()
settings.addAll(newSettings)
val uiSettings = newSettings.map { item ->
item.asPresentation(resources)
}
_screenState.setValue { old -> old.copy(settings = uiSettings) }
val uiSettings = newSettings.map { it.asPresentation(resources) }
screenState.setValue { old -> old.copy(settings = uiSettings) }
}
}
@@ -0,0 +1,20 @@
package dev.meloda.fast.settings.model
import android.os.Bundle
sealed class SettingsIntent {
data object BackClick : SettingsIntent()
data class ItemClick(val key: String) : SettingsIntent()
data class ItemLongClick(val key: String) : SettingsIntent()
data class ItemValueChanged(val key: String, val newValue: Any?) : SettingsIntent()
data object ConsumePerformHaptic : SettingsIntent()
sealed class Dialog : SettingsIntent() {
data object Dismiss : Dialog()
data class ConfirmClick(val bundle: Bundle? = null) : Dialog()
data object CancelClick : Dialog()
data class ItemPick(val bundle: Bundle? = null) : Dialog()
}
}
@@ -1,13 +1,13 @@
package dev.meloda.fast.settings.model
import android.content.res.Resources
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import dev.meloda.fast.common.model.UiText
import dev.meloda.fast.common.model.parseString
import dev.meloda.fast.datastore.AppSettings
import kotlin.reflect.KClass
@Immutable
@Stable
sealed class SettingsItem<T>(
val key: String,
value: T,
@@ -0,0 +1,8 @@
package dev.meloda.fast.settings.model
sealed class SettingsNavigationIntent {
data object Back : SettingsNavigationIntent()
data object Language : SettingsNavigationIntent()
data object Restart : SettingsNavigationIntent()
data object LogOut : SettingsNavigationIntent()
}
@@ -6,13 +6,13 @@ import dev.meloda.fast.datastore.AppSettings
@Immutable
data class SettingsScreenState(
val settings: List<UiItem>,
val showDebugOptions: Boolean
val dialog: SettingsDialog?
) {
companion object {
val EMPTY: SettingsScreenState = SettingsScreenState(
settings = emptyList(),
showDebugOptions = AppSettings.Debug.showDebugCategory
dialog = null
)
}
}
@@ -1,26 +1,37 @@
package dev.meloda.fast.settings.navigation
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import dev.meloda.fast.settings.SettingsViewModel
import dev.meloda.fast.settings.model.SettingsNavigationIntent
import dev.meloda.fast.settings.presentation.SettingsRoute
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@Serializable
object Settings
fun NavGraphBuilder.settingsScreen(
onBack: () -> Unit,
onLogOutButtonClicked: () -> Unit,
onLanguageItemClicked: () -> Unit,
onRestartRequired: () -> Unit,
handleNavigationIntent: (SettingsNavigationIntent) -> Unit
) {
composable<Settings> {
val viewModel: SettingsViewModel = koinViewModel()
val screenState by viewModel.screenStateFlow.collectAsStateWithLifecycle()
val hapticType by viewModel.hapticTypeFlow.collectAsStateWithLifecycle()
val navigationIntent by viewModel.navigationIntentFlow.collectAsStateWithLifecycle()
LaunchedEffect(navigationIntent) {
navigationIntent?.let(handleNavigationIntent)
}
SettingsRoute(
onBack = onBack,
onLogOutButtonClicked = onLogOutButtonClicked,
onLanguageItemClicked = onLanguageItemClicked,
onRestartRequired = onRestartRequired
handleIntent = viewModel::handleIntent,
screenState = screenState,
hapticType = hapticType
)
}
}
@@ -3,7 +3,6 @@ package dev.meloda.fast.settings.presentation
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -26,20 +25,18 @@ import androidx.core.os.bundleOf
import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.datastore.SettingsKeys
import dev.meloda.fast.settings.model.SettingsDialog
import dev.meloda.fast.settings.model.SettingsIntent
import dev.meloda.fast.settings.model.SettingsScreenState
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.components.ActionInvokeDismiss
import dev.meloda.fast.ui.components.MaterialDialog
import dev.meloda.fast.ui.R
@Composable
fun HandleDialogs(
handleIntent: (SettingsIntent.Dialog) -> Unit,
screenState: SettingsScreenState,
dialog: SettingsDialog?,
onConfirmed: (SettingsDialog, Bundle) -> Unit = { _, _ -> },
onDismissed: (SettingsDialog) -> Unit = {},
onItemPicked: (SettingsDialog, Bundle) -> Unit = { _, _ -> }
) {
if (dialog == null) return
val dialog = screenState.dialog ?: return
val context = LocalContext.current
@@ -48,13 +45,13 @@ fun HandleDialogs(
val isEasterEgg = UserConfig.userId == SettingsKeys.ID_DMITRY
MaterialDialog(
onDismissRequest = { onDismissed(dialog) },
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
title = stringResource(
id = if (isEasterEgg) R.string.easter_egg_log_out_dmitry
else R.string.sign_out_confirm_title
),
text = stringResource(id = R.string.sign_out_confirm),
confirmAction = { onConfirmed(dialog, bundleOf()) },
confirmAction = { handleIntent(SettingsIntent.Dialog.ConfirmClick()) },
confirmText = stringResource(
id = if (isEasterEgg) R.string.easter_egg_log_out_dmitry
else R.string.action_sign_out
@@ -66,10 +63,10 @@ fun HandleDialogs(
is SettingsDialog.PerformCrash -> {
MaterialDialog(
onDismissRequest = { onDismissed(dialog) },
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
title = "Perform crash",
text = "App will be crashed. Are you sure?",
confirmAction = { onConfirmed(dialog, bundleOf()) },
confirmAction = { handleIntent(SettingsIntent.Dialog.ConfirmClick()) },
confirmText = stringResource(id = R.string.yes),
cancelText = stringResource(id = R.string.cancel),
actionInvokeDismiss = ActionInvokeDismiss.Always
@@ -88,17 +85,18 @@ fun HandleDialogs(
}
MaterialDialog(
onDismissRequest = { onDismissed(dialog) },
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
title = "Import auth data",
confirmAction = {
onConfirmed(
dialog,
handleIntent(
SettingsIntent.Dialog.ConfirmClick(
bundleOf(
"ACCESS_TOKEN" to accessToken,
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
)
)
)
},
confirmText = "Import",
cancelText = stringResource(R.string.cancel)
@@ -198,17 +196,18 @@ fun HandleDialogs(
}
MaterialDialog(
onDismissRequest = { onDismissed(dialog) },
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
title = "Export auth data",
confirmAction = {
onConfirmed(
dialog,
handleIntent(
SettingsIntent.Dialog.ConfirmClick(
bundleOf(
"ACCESS_TOKEN" to accessToken,
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
)
)
)
},
confirmText = stringResource(R.string.ok),
) {
@@ -269,7 +268,8 @@ fun HandleDialogs(
"Auth data copied to clipboard. Be careful with this data. If another person gets it, your account will be at risk",
Toast.LENGTH_LONG
).show()
onDismissed(dialog)
handleIntent(SettingsIntent.Dialog.Dismiss)
},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
@@ -1,65 +1,24 @@
package dev.meloda.fast.settings.presentation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.meloda.fast.datastore.SettingsKeys
import dev.meloda.fast.settings.SettingsViewModel
import dev.meloda.fast.settings.model.SettingsDialog
import org.koin.compose.viewmodel.koinViewModel
import dev.meloda.fast.settings.model.HapticType
import dev.meloda.fast.settings.model.SettingsIntent
import dev.meloda.fast.settings.model.SettingsScreenState
@Composable
fun SettingsRoute(
onBack: () -> Unit,
onLogOutButtonClicked: () -> Unit,
onLanguageItemClicked: () -> Unit,
onRestartRequired: () -> Unit,
viewModel: SettingsViewModel = koinViewModel()
handleIntent: (SettingsIntent) -> Unit,
screenState: SettingsScreenState,
hapticType: HapticType?
) {
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val hapticType by viewModel.hapticType.collectAsStateWithLifecycle()
val dialog by viewModel.dialog.collectAsStateWithLifecycle()
val isNeedToRestart by viewModel.isNeedToRestart.collectAsStateWithLifecycle()
LaunchedEffect(isNeedToRestart) {
if (isNeedToRestart) {
onRestartRequired()
}
}
SettingsScreen(
handleIntent = handleIntent,
screenState = screenState,
hapticType = hapticType,
onBack = onBack,
onHapticPerformed = viewModel::onHapticPerformed,
onSettingsItemClicked = { key ->
when (key) {
SettingsKeys.KEY_APPEARANCE_LANGUAGE -> {
onLanguageItemClicked()
}
else -> viewModel.onSettingsItemClicked(key)
}
},
onSettingsItemLongClicked = viewModel::onSettingsItemLongClicked,
onSettingsItemValueChanged = viewModel::onSettingsItemChanged
hapticType = hapticType
)
HandleDialogs(
handleIntent = handleIntent,
screenState = screenState,
dialog = dialog,
onConfirmed = { dialog, bundle ->
when (dialog) {
is SettingsDialog.LogOut -> {
onLogOutButtonClicked()
}
else -> Unit
}
viewModel.onDialogConfirmed(dialog, bundle)
},
onDismissed = viewModel::onDialogDismissed,
onItemPicked = viewModel::onDialogItemPicked
)
}
@@ -22,7 +22,9 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
@@ -36,6 +38,7 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.settings.model.HapticType
import dev.meloda.fast.settings.model.SettingsIntent
import dev.meloda.fast.settings.model.SettingsScreenState
import dev.meloda.fast.settings.model.UiItem
import dev.meloda.fast.settings.presentation.item.ListItem
@@ -43,8 +46,8 @@ import dev.meloda.fast.settings.presentation.item.SwitchItem
import dev.meloda.fast.settings.presentation.item.TextFieldItem
import dev.meloda.fast.settings.presentation.item.TitleItem
import dev.meloda.fast.settings.presentation.item.TitleTextItem
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.theme.LocalThemeConfig
@OptIn(
@@ -53,22 +56,30 @@ import dev.meloda.fast.ui.R
)
@Composable
fun SettingsScreen(
handleIntent: (SettingsIntent) -> Unit,
screenState: SettingsScreenState = SettingsScreenState.EMPTY,
hapticType: HapticType? = null,
onBack: () -> Unit = {},
onHapticPerformed: () -> Unit = {},
onSettingsItemClicked: (key: String) -> Unit = {},
onSettingsItemLongClicked: (key: String) -> Unit = {},
onSettingsItemValueChanged: (key: String, newValue: Any?) -> Unit = { _, _ -> }
hapticType: HapticType?
) {
val view = LocalView.current
val onSettingsItemClicked by rememberUpdatedState { key: String ->
handleIntent(SettingsIntent.ItemClick(key))
}
val onSettingsItemLongClicked by rememberUpdatedState { key: String ->
handleIntent(SettingsIntent.ItemLongClick(key))
}
val onSettingsItemValueChanged by rememberUpdatedState { key: String, newValue: Any? ->
handleIntent(SettingsIntent.ItemValueChanged(key, newValue))
}
LaunchedEffect(hapticType) {
if (hapticType != null) {
if (AppSettings.General.enableHaptic) {
view.performHapticFeedback(hapticType.getHaptic())
}
onHapticPerformed()
handleIntent(SettingsIntent.ConsumePerformHaptic)
}
}
@@ -90,7 +101,7 @@ fun SettingsScreen(
)
},
navigationIcon = {
IconButton(onClick = onBack) {
IconButton(onClick = { handleIntent(SettingsIntent.BackClick) }) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_back_round_24),
contentDescription = "Back button"