refactor: improve auth screen and bump haze version
* Bump haze version to 1.6.0. * Blur now works on android 11 and older * Add "Sign up" and "Forgot password?" links to the auth screen. * Add logic to toggle dynamic colors on logo click in the auth screen (Android 12+).
This commit is contained in:
@@ -68,7 +68,7 @@ fun MainScreen(
|
||||
) {
|
||||
val activity = LocalActivity.current as? AppCompatActivity ?: return
|
||||
val theme = LocalThemeConfig.current
|
||||
val hazeState = remember { HazeState() }
|
||||
val hazeState = remember { HazeState(true) }
|
||||
val navController = rememberNavController()
|
||||
|
||||
var selectedItemIndex by rememberSaveable {
|
||||
|
||||
@@ -130,7 +130,7 @@ val LocalSizeConfig = compositionLocalOf {
|
||||
)
|
||||
}
|
||||
|
||||
val LocalHazeState = compositionLocalOf { HazeState() }
|
||||
val LocalHazeState = compositionLocalOf { HazeState(true) }
|
||||
val LocalBottomPadding = compositionLocalOf { 0.dp }
|
||||
val LocalUser = compositionLocalOf<VkUser?> { null }
|
||||
val LocalReselectedTab = compositionLocalOf { mapOf<Any, Boolean>() }
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
<string name="settings_general_show_emoji_button_summary">Показывать кнопку эмоджи на панели чата</string>
|
||||
<string name="settings_features_show_time_in_action_messages_title">Показывать время в сервисных сообщениях</string>
|
||||
<string name="settings_experimental_use_blur_title">Использовать размытие</string>
|
||||
<string name="settings_experimental_use_blur_summary">Добавлять размытие везде, где возможно.\\nРаботает только с 12 версии Android</string>
|
||||
<string name="settings_experimental_use_blur_summary">Добавлять размытие везде, где возможно</string>
|
||||
<string name="settings_experimental_more_animations_title">Больше анимаций</string>
|
||||
<string name="warning_confirmation">Подтверждение</string>
|
||||
<string name="captcha_exit_warning">Вы уверены? Процесс ввода капчи будет отменён</string>
|
||||
@@ -269,4 +269,6 @@
|
||||
<string name="italic">Курсив</string>
|
||||
<string name="underline">Подчёркнутый</string>
|
||||
<string name="link">Ссылка</string>
|
||||
<string name="login_sign_up">Регистрация</string>
|
||||
<string name="login_forgot_password">Забыли пароль?</string>
|
||||
</resources>
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
<string name="settings_features_long_poll_in_background_summary">Your messages will be updating even when app is not on the screen</string>
|
||||
<string name="settings_features_show_time_in_action_messages_title">Show time in action messages</string>
|
||||
<string name="settings_experimental_use_blur_title">Use blur</string>
|
||||
<string name="settings_experimental_use_blur_summary">Adds blur wherever possible.\nWorks on android 12 and newer</string>
|
||||
<string name="settings_experimental_use_blur_summary">Adds blur wherever possible</string>
|
||||
<string name="settings_experimental_more_animations_title">More animations</string>
|
||||
<string name="settings_experimental_more_animations_summary">Use animations wherever possible</string>
|
||||
|
||||
@@ -340,9 +340,11 @@
|
||||
<string name="unspam_message_text">Are you sure you want to unmark this message as spam?</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
|
||||
<string name="autofill">Autofill</string>
|
||||
<string name="autofill" tools:ignore="PrivateResource">Autofill</string>
|
||||
<string name="bold">Bold</string>
|
||||
<string name="italic">Italic</string>
|
||||
<string name="underline">Underline</string>
|
||||
<string name="link">Link</string>
|
||||
<string name="login_sign_up">Sign up</string>
|
||||
<string name="login_forgot_password">Forgot password?</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.meloda.fast.auth.login
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
@@ -24,6 +25,7 @@ import dev.meloda.fast.data.db.AccountsRepository
|
||||
import dev.meloda.fast.data.processState
|
||||
import dev.meloda.fast.data.success
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.datastore.UserSettings
|
||||
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
||||
import dev.meloda.fast.domain.OAuthUseCase
|
||||
import dev.meloda.fast.model.database.AccountEntity
|
||||
@@ -62,6 +64,8 @@ interface LoginViewModel {
|
||||
|
||||
fun onSignInButtonClicked()
|
||||
|
||||
fun onLogoClicked()
|
||||
|
||||
fun onNavigatedToMain()
|
||||
fun onNavigatedToUserBanned()
|
||||
fun onNavigatedToCaptcha()
|
||||
@@ -79,7 +83,8 @@ class LoginViewModelImpl(
|
||||
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
||||
private val accountsRepository: AccountsRepository,
|
||||
private val loginValidator: LoginValidator,
|
||||
private val longPollController: LongPollController
|
||||
private val longPollController: LongPollController,
|
||||
private val userSettings: UserSettings
|
||||
) : ViewModel(), LoginViewModel {
|
||||
|
||||
override val screenState = MutableStateFlow(LoginScreenState.EMPTY)
|
||||
@@ -164,6 +169,14 @@ class LoginViewModelImpl(
|
||||
login()
|
||||
}
|
||||
|
||||
override fun onLogoClicked() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
userSettings.onEnableDynamicColorsChanged(
|
||||
!userSettings.enableDynamicColors.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigatedToMain() {
|
||||
isNeedToOpenMain.update { false }
|
||||
}
|
||||
@@ -210,7 +223,7 @@ class LoginViewModelImpl(
|
||||
processValidation()
|
||||
if (!validationState.value.contains(LoginValidationResult.Valid)) return
|
||||
|
||||
screenState.updateValue { copy(isLoading = false) }
|
||||
screenState.updateValue { copy(isLoading = true) }
|
||||
|
||||
val currentValidationSid = validationSid.value
|
||||
val currentValidationCode = validationCode.value?.takeIf { currentValidationSid != null }
|
||||
|
||||
+94
-17
@@ -1,5 +1,6 @@
|
||||
package dev.meloda.fast.auth.login.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@@ -8,6 +9,7 @@ import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -27,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScaffoldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -37,18 +40,20 @@ import androidx.compose.ui.autofill.ContentType
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentType
|
||||
import androidx.compose.ui.semantics.password
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import dev.meloda.fast.auth.login.LoginViewModel
|
||||
import dev.meloda.fast.auth.login.LoginViewModelImpl
|
||||
@@ -126,7 +131,8 @@ fun LoginRoute(
|
||||
onPasswordFieldEnterKeyClicked = viewModel::onSignInButtonClicked,
|
||||
onPasswordVisibilityButtonClicked = viewModel::onPasswordVisibilityButtonClicked,
|
||||
onPasswordFieldGoAction = viewModel::onSignInButtonClicked,
|
||||
onSignInButtonClicked = viewModel::onSignInButtonClicked
|
||||
onSignInButtonClicked = viewModel::onSignInButtonClicked,
|
||||
onLogoClicked = viewModel::onLogoClicked
|
||||
)
|
||||
|
||||
HandleDialogs(
|
||||
@@ -144,13 +150,21 @@ fun LoginScreen(
|
||||
onPasswordFieldEnterKeyClicked: () -> Unit = {},
|
||||
onPasswordVisibilityButtonClicked: () -> Unit = {},
|
||||
onPasswordFieldGoAction: () -> Unit = {},
|
||||
onSignInButtonClicked: () -> Unit = {}
|
||||
onSignInButtonClicked: () -> Unit = {},
|
||||
onLogoClicked: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val size = LocalSizeConfig.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val titleSpacerSize by animateDpAsState(if (size.isHeightSmall) 24.dp else 58.dp)
|
||||
val bottomPadding by animateDpAsState(if (size.isHeightSmall) 10.dp else 30.dp)
|
||||
val titleSpacerSize by animateDpAsState(
|
||||
targetValue = if (size.isHeightSmall) 24.dp else 58.dp,
|
||||
label = "title spacer size"
|
||||
)
|
||||
val bottomPadding by animateDpAsState(
|
||||
targetValue = if (size.isHeightSmall) 10.dp else 30.dp,
|
||||
label = "bottom padding"
|
||||
)
|
||||
|
||||
val (loginFocusable, passwordFocusable) =
|
||||
FocusRequester.createRefs()
|
||||
@@ -163,15 +177,15 @@ fun LoginScreen(
|
||||
.padding(padding)
|
||||
.padding(top = 30.dp)
|
||||
.padding(horizontal = 30.dp)
|
||||
.padding(bottom = bottomPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = screenState.showLogo,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
exit = fadeOut(),
|
||||
label = "Logo visibility"
|
||||
) {
|
||||
Logo()
|
||||
Logo(onLogoClicked = onLogoClicked)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
@@ -180,7 +194,8 @@ fun LoginScreen(
|
||||
.align(Alignment.Center),
|
||||
visible = !screenState.showLogo,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
exit = fadeOut(),
|
||||
label = "Login visibility"
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -235,7 +250,10 @@ fun LoginScreen(
|
||||
isError = screenState.loginError,
|
||||
singleLine = true
|
||||
)
|
||||
AnimatedVisibility(visible = screenState.loginError) {
|
||||
AnimatedVisibility(
|
||||
visible = screenState.loginError,
|
||||
label = "Login error visibility"
|
||||
) {
|
||||
TextFieldErrorText(text = stringResource(id = UiR.string.error_empty_field))
|
||||
}
|
||||
|
||||
@@ -300,16 +318,18 @@ fun LoginScreen(
|
||||
},
|
||||
singleLine = true
|
||||
)
|
||||
AnimatedVisibility(visible = screenState.passwordError) {
|
||||
AnimatedVisibility(
|
||||
visible = screenState.passwordError,
|
||||
label = "Password error visibility"
|
||||
) {
|
||||
TextFieldErrorText(text = stringResource(id = UiR.string.error_empty_field))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box(
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
contentAlignment = Alignment.Center
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
@@ -324,7 +344,8 @@ fun LoginScreen(
|
||||
AnimatedVisibility(
|
||||
visible = screenState.isLoading,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
exit = fadeOut(),
|
||||
label = "Progress indicator visibility"
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
@@ -332,7 +353,8 @@ fun LoginScreen(
|
||||
AnimatedVisibility(
|
||||
visible = !screenState.isLoading,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
exit = fadeOut(),
|
||||
label = "Sign in icon visibility"
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = UiR.drawable.ic_arrow_end),
|
||||
@@ -341,12 +363,57 @@ fun LoginScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = screenState.showLogo,
|
||||
label = "Bottom padding visibility"
|
||||
) {
|
||||
Spacer(Modifier.height(bottomPadding))
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !screenState.showLogo,
|
||||
label = "Spacer between fab and bottom text buttons visibility"
|
||||
) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !screenState.showLogo,
|
||||
label = "Text button row visibility"
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, "https://vk.com/join".toUri())
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(UiR.string.login_sign_up))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "•",
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, "https://vk.com/restore".toUri())
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(UiR.string.login_forgot_password))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun HandleDialogs(
|
||||
loginDialog: LoginDialog?,
|
||||
@@ -368,3 +435,13 @@ fun HandleDialogs(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun LoginScreenPreview() {
|
||||
LoginScreen(
|
||||
screenState = LoginScreenState.EMPTY.copy(
|
||||
showLogo = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package dev.meloda.fast.auth.login.presentation
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.animateIntAsState
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -26,22 +24,20 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.meloda.fast.datastore.UserSettings
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.theme.LocalSizeConfig
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Logo(modifier: Modifier = Modifier) {
|
||||
fun Logo(
|
||||
modifier: Modifier = Modifier,
|
||||
onLogoClicked: () -> Unit = {}
|
||||
) {
|
||||
val size = LocalSizeConfig.current
|
||||
|
||||
val iconWidth by animateDpAsState(if (size.isWidthSmall) 110.dp else 134.dp)
|
||||
val appNameFontSize by animateIntAsState(if (size.isWidthSmall) 32 else 38)
|
||||
val bottomAdditionalPadding by animateDpAsState(if (size.isHeightSmall) 10.dp else 30.dp)
|
||||
|
||||
val userSettings: UserSettings = koinInject()
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
@@ -65,13 +61,7 @@ fun Logo(modifier: Modifier = Modifier) {
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
onLongClick = null,
|
||||
onClick = {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
userSettings.onEnableDynamicColorsChanged(
|
||||
!userSettings.enableDynamicColors.value
|
||||
)
|
||||
}
|
||||
}
|
||||
onClick = onLogoClicked
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -23,5 +23,4 @@ class LoginValidator {
|
||||
|
||||
return resultList
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ fun ChatMaterialsScreen(
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val currentTheme = LocalThemeConfig.current
|
||||
val hazeState = remember { HazeState() }
|
||||
val hazeState = remember { HazeState(true) }
|
||||
|
||||
val titles = remember {
|
||||
listOf(
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ fun MessagesHistoryScreen(
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val theme = LocalThemeConfig.current
|
||||
val listState = rememberLazyListState()
|
||||
val hazeState = remember { HazeState() }
|
||||
val hazeState = remember { HazeState(true) }
|
||||
|
||||
LaunchedEffect(scrollIndex) {
|
||||
if (scrollIndex != null) {
|
||||
|
||||
@@ -416,8 +416,7 @@ class SettingsViewModelImpl(
|
||||
key = SettingsKeys.KEY_USE_BLUR,
|
||||
defaultValue = SettingsKeys.DEFAULT_USE_BLUR,
|
||||
title = UiText.Resource(UiR.string.settings_experimental_use_blur_title),
|
||||
text = UiText.Resource(UiR.string.settings_experimental_use_blur_summary),
|
||||
isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
text = UiText.Resource(UiR.string.settings_experimental_use_blur_summary)
|
||||
)
|
||||
val enableAnimations = SettingsItem.Switch(
|
||||
key = SettingsKeys.KEY_MORE_ANIMATIONS,
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ fun SettingsScreen(
|
||||
|
||||
val themeConfig = LocalThemeConfig.current
|
||||
|
||||
val hazeState = remember { HazeState() }
|
||||
val hazeState = remember { HazeState(true) }
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
||||
@@ -8,7 +8,7 @@ versionName = "0.2.2"
|
||||
agp = "8.10.0"
|
||||
converterMoshi = "2.11.0"
|
||||
eithernet = "2.0.0"
|
||||
haze = "1.5.4"
|
||||
haze = "1.6.0"
|
||||
kotlin = "2.1.20"
|
||||
ksp = "2.1.20-2.0.1"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user