login refactoring
This commit is contained in:
parent
fff16ca0fb
commit
5b27c8783e
3 changed files with 93 additions and 40 deletions
|
@ -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\"")
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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<String?>(null)
|
||||
val error: LiveData<String?> = _error // TODO put this in uistate
|
||||
val error: LiveData<String?> = _error
|
||||
|
||||
private val _apiKey = MutableStateFlow<String>("")
|
||||
var apiKey: StateFlow<String> = _apiKey.asStateFlow()
|
||||
|
||||
private val applicationContext = getApplication<Application>().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()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue