From 5b27c8783e85b876f8d4dab5b979f9a04784f410 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sun, 4 Aug 2024 09:25:31 +0200 Subject: [PATCH] login refactoring --- app/build.gradle.kts | 1 + .../vastapp/activity/login/LoginActivity.kt | 96 ++++++++++++------- .../vastapp/activity/login/LoginViewModel.kt | 36 +++++-- 3 files changed, 93 insertions(+), 40 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b816cbc..fc64302 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,6 +21,7 @@ android { } // don't forget to add another "s this is counter intuitive I know but not my fault + buildConfigField("Boolean", "AUTO_LOGIN", "true") buildConfigField("String", "VASTAI_KEY", "\"\"") buildConfigField("String", "VASIAI_API_ENDPOINT", "\"https://cloud.vast.ai/api/v0\"") } diff --git a/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt index fa0ad27..eb706fd 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt @@ -92,7 +92,8 @@ class LoginActivity : ComponentActivity() { } } - loginViewModel.loadKey() + if (BuildConfig.AUTO_LOGIN) + loginViewModel.loadKey() enableEdgeToEdge() setContent { @@ -128,17 +129,11 @@ class LoginActivity : ComponentActivity() { fun LoginApp(loginViewModel: LoginViewModel) { val uiState by loginViewModel.uiState.collectAsState() val loginErrorMessage by loginViewModel.error.observeAsState() // TODO put this in uiState + val apiKey by loginViewModel.apiKey.collectAsState() + val isIdle by remember(uiState) { derivedStateOf { uiState !is LoginUiState.Loading } } - - var apiKey by rememberSaveable { mutableStateOf(BuildConfig.VASTAI_KEY) } var advancedOpen by rememberSaveable { mutableStateOf(false) } - val transition = updateTransition(targetState = advancedOpen, label = "Advanced Menu Transition") - val arrowRotation by transition.animateFloat(label = "Advanced Menu Arrow Rotation") { state -> - if (state) 180f else 0f - } - - Column( modifier = Modifier .width(300.dp) @@ -146,38 +141,25 @@ fun LoginApp(loginViewModel: LoginViewModel) { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - TextField( - modifier = Modifier.fillMaxWidth(), + KeyTextField( enabled = isIdle, - value = apiKey, - onValueChange = { apiKey = it }, - label = { Text(text = stringResource(id = R.string.api_key)) }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - textStyle = if (uiState is LoginUiState.Success) rainbowTextStyle() else LocalTextStyle.current, - singleLine = true, + apiKey = apiKey, + onValueChange = { loginViewModel.onApiKeyChange(it) }, + rainbowText = uiState is LoginUiState.Success, isError = loginErrorMessage != null ) Spacer(modifier = Modifier.height(10.dp)) Row { - TextButton( + AdvancedOptionsButton( enabled = isIdle, - onClick = { - advancedOpen = !advancedOpen - } - ) { - Text(text = stringResource(id = R.string.advanced_options)) - Icon( - imageVector = Icons.Filled.KeyboardArrowDown, - contentDescription = null, - modifier = Modifier.rotate(arrowRotation) - ) - } + onClick = { advancedOpen = !advancedOpen }, + isOpen = advancedOpen + ) Spacer(modifier = Modifier.weight(1f)) Button( enabled = isIdle, onClick = { - loginViewModel.tryLogin(apiKey) + loginViewModel.tryLogin() } ) { if (uiState is LoginUiState.Loading) { @@ -187,12 +169,62 @@ fun LoginApp(loginViewModel: LoginViewModel) { } } } + AnimatedVisibility(visible = advancedOpen) { AdvancedOptions() } } } +@Composable +fun KeyTextField( + enabled: Boolean, + apiKey: String, + onValueChange: (String) -> Unit, + rainbowText: Boolean, + isError: Boolean +) { + + TextField( + modifier = Modifier.fillMaxWidth(), + label = { Text(text = stringResource(id = R.string.api_key)) }, + + visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + textStyle = if (rainbowText) rainbowTextStyle() else LocalTextStyle.current, + singleLine = true, + + enabled = enabled, + value = apiKey, + onValueChange = onValueChange, + isError = isError + ) +} + +@Composable +fun AdvancedOptionsButton( + enabled: Boolean, + onClick: () -> Unit, + isOpen: Boolean +) { + val transition = updateTransition(targetState = isOpen, label = "Advanced Menu Transition") + val arrowRotation by transition.animateFloat(label = "Advanced Menu Arrow Rotation") { state -> + if (state) 180f else 0f + } + + TextButton( + enabled = enabled, + onClick = onClick + ) { + Text(text = stringResource(id = R.string.advanced_options)) + Icon( + imageVector = Icons.Filled.KeyboardArrowDown, + contentDescription = null, + modifier = Modifier.rotate(arrowRotation) + ) + } +} + @Composable fun rainbowTextStyle(): TextStyle { return LocalTextStyle.current.copy(brush = Brush.linearGradient(colors = listOf( @@ -207,6 +239,6 @@ fun rainbowTextStyle(): TextStyle { } @Composable -fun AdvancedOptions() { // TODO put this in viewmodel +fun AdvancedOptions() { FunGame() } \ No newline at end of file diff --git a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt index baa4066..c703080 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt @@ -11,6 +11,7 @@ import eu.m724.vastapp.vastai.api.UserUrlRequestCallback import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import org.chromium.net.CronetEngine import java.util.concurrent.Executor @@ -25,7 +26,10 @@ class LoginViewModel( _uiState.asStateFlow() private val _error = MutableLiveData(null) - val error: LiveData = _error // TODO put this in uistate + val error: LiveData = _error + + private val _apiKey = MutableStateFlow("") + var apiKey: StateFlow = _apiKey.asStateFlow() private val applicationContext = getApplication().applicationContext private val sharedPreferences = applicationContext.getSharedPreferences("login", Context.MODE_PRIVATE) @@ -34,19 +38,34 @@ class LoginViewModel( val apiKey = sharedPreferences.getString("apiKey", null) if (apiKey != null) { - tryLogin(apiKey) + _apiKey.value = apiKey + tryLogin(LoginUiState.FullLoading) } } - fun tryLogin(apiKey: String) { + fun tryLogin() { + tryLogin(LoginUiState.Loading) + } + + fun onApiKeyChange(apiKey: String) { + _apiKey.update { apiKey } + } + + private fun saveKey() { + with (sharedPreferences.edit()) { + putString("apiKey", apiKey.value) // TODO encrypt + apply() + } + } + + private fun tryLogin(initialState: LoginUiState) { + val apiKey = apiKey.value + val vastApi = VastApi(apiKey, cronetEngine, executor) val request = vastApi.buildRequest( ApiRoute.SHOW_USER, UserUrlRequestCallback({ user -> - with (sharedPreferences.edit()) { - putString("apiKey", apiKey) // TODO encrypt - apply() - } // TODO toggle for this + saveKey() // TODO toggle for this _uiState.value = LoginUiState.Success(user) }, { apiFailure -> _uiState.value = LoginUiState.Idle @@ -54,7 +73,8 @@ class LoginViewModel( }) ) - _uiState.value = LoginUiState.Loading + + _uiState.value = initialState request.start() } } \ No newline at end of file