forked from melod1n/fast-messenger
refactor(settings): route settings UI through intents and navigation effects
This commit is contained in:
@@ -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 = {
|
||||
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)
|
||||
activity.finish()
|
||||
handleNavigationIntent = { intent ->
|
||||
when (intent) {
|
||||
SettingsNavigationIntent.Back -> navController.navigateUp()
|
||||
SettingsNavigationIntent.Language -> navController.navigateToLanguagePicker()
|
||||
SettingsNavigationIntent.Restart -> {
|
||||
activity?.let {
|
||||
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,
|
||||
|
||||
+8
@@ -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()
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-8
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-26
@@ -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,15 +85,16 @@ fun HandleDialogs(
|
||||
}
|
||||
|
||||
MaterialDialog(
|
||||
onDismissRequest = { onDismissed(dialog) },
|
||||
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
|
||||
title = "Import auth data",
|
||||
confirmAction = {
|
||||
onConfirmed(
|
||||
dialog,
|
||||
bundleOf(
|
||||
"ACCESS_TOKEN" to accessToken,
|
||||
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
|
||||
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
|
||||
handleIntent(
|
||||
SettingsIntent.Dialog.ConfirmClick(
|
||||
bundleOf(
|
||||
"ACCESS_TOKEN" to accessToken,
|
||||
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
|
||||
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
@@ -198,15 +196,16 @@ fun HandleDialogs(
|
||||
}
|
||||
|
||||
MaterialDialog(
|
||||
onDismissRequest = { onDismissed(dialog) },
|
||||
onDismissRequest = { handleIntent(SettingsIntent.Dialog.Dismiss) },
|
||||
title = "Export auth data",
|
||||
confirmAction = {
|
||||
onConfirmed(
|
||||
dialog,
|
||||
bundleOf(
|
||||
"ACCESS_TOKEN" to accessToken,
|
||||
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
|
||||
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
|
||||
handleIntent(
|
||||
SettingsIntent.Dialog.ConfirmClick(
|
||||
bundleOf(
|
||||
"ACCESS_TOKEN" to accessToken,
|
||||
"EXCHANGE_TOKEN" to exchangeToken.ifEmpty { null },
|
||||
"TRUSTED_HASH" to trustedHash.ifEmpty { null }
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
@@ -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)
|
||||
) {
|
||||
|
||||
+9
-50
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
+20
-9
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user