twoFa -> validation naming; fixes for preview for screens (separating view model from ui); some improvements & fixes

This commit is contained in:
2024-07-13 22:45:49 +03:00
parent dfdc48b682
commit 733627f935
98 changed files with 1611 additions and 1637 deletions
@@ -1,9 +1,13 @@
package com.meloda.app.fast.languagepicker
import android.content.res.Resources
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.ViewModel
import com.meloda.app.fast.common.UiText
import com.meloda.app.fast.common.extensions.setValue
import com.meloda.app.fast.common.parseString
import com.meloda.app.fast.designsystem.R
import com.meloda.app.fast.languagepicker.model.LanguagePickerScreenState
import com.meloda.app.fast.languagepicker.model.SelectableLanguage
import kotlinx.coroutines.flow.MutableStateFlow
@@ -12,25 +16,54 @@ import kotlinx.coroutines.flow.StateFlow
interface LanguagePickerViewModel {
val screenState: StateFlow<LanguagePickerScreenState>
fun setLanguages(languages: List<SelectableLanguage>)
fun onLanguagePicked(newLanguage: SelectableLanguage)
fun onApplyButtonClicked()
fun updateCurrentLocale(locale: String)
}
class LanguagePickerViewModelImpl : LanguagePickerViewModel, ViewModel() {
class LanguagePickerViewModelImpl(
private val resources: Resources
) : LanguagePickerViewModel, ViewModel() {
override val screenState = MutableStateFlow(
LanguagePickerScreenState(
languages = emptyList(),
currentLanguage = AppCompatDelegate.getApplicationLocales().toLanguageTags()
)
)
override val screenState = MutableStateFlow(LanguagePickerScreenState.EMPTY)
init {
val languages = listOf(
Triple(
"",
UiText.Resource(R.string.language_key_system),
UiText.Resource(R.string.language_system)
),
Triple(
"en-US",
UiText.Resource(R.string.language_key_english),
UiText.Resource(R.string.language_english),
),
Triple(
"ru-RU",
UiText.Resource(R.string.language_key_russian),
UiText.Resource(R.string.language_russian)
),
Triple(
"uk-UA",
UiText.Resource(R.string.language_key_ukrainian),
UiText.Resource(R.string.language_ukrainian)
)
).map { (key, language, local) ->
Triple(
key,
language.parseString(resources).orEmpty(),
local.parseString(resources).orEmpty()
)
}.map { (key, language, local) ->
SelectableLanguage(
local = local,
language = language,
key = key,
isSelected = key == AppCompatDelegate.getApplicationLocales().toLanguageTags()
)
}
override fun setLanguages(languages: List<SelectableLanguage>) {
screenState.setValue { old -> old.copy(languages = languages) }
}
@@ -6,4 +6,12 @@ import androidx.compose.runtime.Immutable
data class LanguagePickerScreenState(
val languages: List<SelectableLanguage>,
val currentLanguage: String?,
)
) {
companion object {
val EMPTY: LanguagePickerScreenState = LanguagePickerScreenState(
languages = emptyList(),
currentLanguage = null
)
}
}
@@ -0,0 +1,22 @@
package com.meloda.app.fast.languagepicker.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.meloda.app.fast.languagepicker.presentation.LanguagePickerRoute
import kotlinx.serialization.Serializable
@Serializable
object LanguagePicker
fun NavGraphBuilder.languagePickerScreen(
onBack: () -> Unit,
) {
composable<LanguagePicker> {
LanguagePickerRoute(onBack = onBack)
}
}
fun NavController.navigateToLanguagePicker() {
this.navigate(LanguagePicker)
}
@@ -1,78 +0,0 @@
package com.meloda.app.fast.languagepicker.navigation
import android.content.res.Resources
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.meloda.app.fast.common.UiText
import com.meloda.app.fast.common.parseString
import com.meloda.app.fast.designsystem.R
import com.meloda.app.fast.languagepicker.LanguagePickerViewModel
import com.meloda.app.fast.languagepicker.LanguagePickerViewModelImpl
import com.meloda.app.fast.languagepicker.model.SelectableLanguage
import com.meloda.app.fast.languagepicker.presentation.LanguagePickerScreen
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@Serializable
object LanguagePicker
private fun getLanguages(resources: Resources): List<SelectableLanguage> {
return listOf(
Triple(
"",
UiText.Resource(R.string.language_key_system),
UiText.Resource(R.string.language_system)
),
Triple(
"en-US",
UiText.Resource(R.string.language_key_english),
UiText.Resource(R.string.language_english),
),
Triple(
"ru-RU",
UiText.Resource(R.string.language_key_russian),
UiText.Resource(R.string.language_russian)
),
Triple(
"uk-UA",
UiText.Resource(R.string.language_key_ukrainian),
UiText.Resource(R.string.language_ukrainian)
)
).map { (key, language, local) ->
Triple(
key,
language.parseString(resources).orEmpty(),
local.parseString(resources).orEmpty()
)
}.map { (key, language, local) ->
SelectableLanguage(
local = local,
language = language,
key = key,
isSelected = key == AppCompatDelegate.getApplicationLocales().toLanguageTags()
)
}
}
fun NavGraphBuilder.languagePickerRoute(
onBack: () -> Unit,
) {
composable<LanguagePicker> {
val languages = getLanguages(LocalContext.current.resources)
val viewModel: LanguagePickerViewModel = koinViewModel<LanguagePickerViewModelImpl>()
viewModel.setLanguages(languages)
LanguagePickerScreen(
onBack = onBack,
viewModel = viewModel
)
}
}
fun NavController.navigateToLanguagePicker() {
this.navigate(LanguagePicker)
}
@@ -56,31 +56,46 @@ import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.meloda.app.fast.languagepicker.LanguagePickerViewModel
import com.meloda.app.fast.languagepicker.LanguagePickerViewModelImpl
import com.meloda.app.fast.languagepicker.model.LanguagePickerScreenState
import com.meloda.app.fast.languagepicker.model.SelectableLanguage
import org.koin.androidx.compose.koinViewModel
import com.meloda.app.fast.designsystem.R as UiR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LanguagePickerScreen(
fun LanguagePickerRoute(
onBack: () -> Unit,
viewModel: LanguagePickerViewModel = koinViewModel<LanguagePickerViewModelImpl>()
) {
val context = LocalContext.current
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val languages = screenState.languages
LifecycleResumeEffect(true) {
viewModel.updateCurrentLocale(AppCompatDelegate.getApplicationLocales().toLanguageTags())
onPauseOrDispose {}
}
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
LanguagePickerScreen(
screenState = screenState,
onBack = onBack,
onLanguagePicked = viewModel::onLanguagePicked,
onApplyButtonClicked = viewModel::onApplyButtonClicked
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LanguagePickerScreen(
screenState: LanguagePickerScreenState = LanguagePickerScreenState.EMPTY,
onBack: () -> Unit = {},
onLanguagePicked: (SelectableLanguage) -> Unit = {},
onApplyButtonClicked: () -> Unit = {}
) {
val context = LocalContext.current
val isButtonEnabled by remember(screenState) {
derivedStateOf {
screenState.currentLanguage != null &&
languages.isNotEmpty() &&
languages.find(SelectableLanguage::isSelected)?.key != screenState.currentLanguage
screenState.languages.isNotEmpty() &&
screenState.languages.find(SelectableLanguage::isSelected)?.key != screenState.currentLanguage
}
}
@@ -165,10 +180,13 @@ fun LanguagePickerScreen(
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(screenState.languages.toList()) { item ->
items(
items = screenState.languages.toList(),
key = SelectableLanguage::key
) { item ->
LanguageItem(
item = item,
onClick = viewModel::onLanguagePicked
onClick = onLanguagePicked
)
}
@@ -183,7 +201,7 @@ fun LanguagePickerScreen(
}
Button(
onClick = viewModel::onApplyButtonClicked,
onClick = onApplyButtonClicked,
enabled = isButtonEnabled,
modifier = Modifier
.fillMaxWidth()