mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Better login page & Quick Connect error handling (#43)
Related to #18 Ensures that when trying to auth via Quick Connect, the button to use a username/password instead is always shown. Additionally, errors that occur during the quick connect process are displayed instead of just logging in the background. This PR does not change the Quick Connect behavior or code, it's just UI improvements for when an error occurs with Quick Connect.
This commit is contained in:
parent
4c2a59a81c
commit
a31cdc79e1
2 changed files with 107 additions and 70 deletions
|
|
@ -8,7 +8,9 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
|
|
@ -26,6 +28,7 @@ import androidx.compose.ui.focus.focusRequester
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.Button
|
||||
|
|
@ -33,6 +36,7 @@ import androidx.tv.material3.MaterialTheme
|
|||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
|
|
@ -142,26 +146,47 @@ fun SwitchUserContent(
|
|||
.fillMaxWidth(),
|
||||
) {
|
||||
if (useQuickConnect) {
|
||||
quickConnect?.let { qc ->
|
||||
if (quickConnect == null && userState !is LoadingState.Error) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(32.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
CircularProgress(Modifier.size(20.dp))
|
||||
Text(
|
||||
text = "Waiting for Quick Connect code...",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
} else if (quickConnect != null) {
|
||||
Text(
|
||||
text = "Use Quick Connect on your device to authenticate to ${server.name ?: server.url}",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = qc.code,
|
||||
text = quickConnect?.code ?: "Failed to get code",
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
useQuickConnect = false
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = "Use username/password")
|
||||
}
|
||||
}
|
||||
UserStateError(userState)
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.cancelQuickConnect()
|
||||
viewModel.clearSwitchUserState()
|
||||
useQuickConnect = false
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = "Use username/password")
|
||||
}
|
||||
} else {
|
||||
var username by remember { mutableStateOf("") }
|
||||
|
|
@ -176,24 +201,7 @@ fun SwitchUserContent(
|
|||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
when (val s = userState) {
|
||||
is LoadingState.Error -> {
|
||||
s.message?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
s.exception?.localizedMessage?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
UserStateError(userState)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.focusGroup(),
|
||||
|
|
@ -259,3 +267,33 @@ fun SwitchUserContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserStateError(
|
||||
userState: LoadingState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (val s = userState) {
|
||||
is LoadingState.Error -> {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
s.message?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
s.exception?.localizedMessage?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,53 +192,52 @@ class SwitchUserViewModel
|
|||
|
||||
fun initiateQuickConnect(server: JellyfinServer) {
|
||||
quickConnectJob?.cancel()
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
val api = jellyfin.createApi(server.url)
|
||||
var state =
|
||||
api
|
||||
.quickConnectApi
|
||||
.initiateQuickConnect()
|
||||
.content
|
||||
withContext(Dispatchers.Main) {
|
||||
quickConnectState.value = state
|
||||
}
|
||||
quickConnectJob =
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
val api = jellyfin.createApi(server.url)
|
||||
var state =
|
||||
api
|
||||
.quickConnectApi
|
||||
.initiateQuickConnect()
|
||||
.content
|
||||
|
||||
quickConnectJob =
|
||||
viewModelScope.launchIO {
|
||||
while (!state.authenticated) {
|
||||
delay(5_000L)
|
||||
state =
|
||||
api.quickConnectApi
|
||||
.getQuickConnectState(
|
||||
secret = state.secret,
|
||||
).content
|
||||
withContext(Dispatchers.Main) {
|
||||
quickConnectState.value = state
|
||||
}
|
||||
}
|
||||
val authenticationResult by api.userApi.authenticateWithQuickConnect(
|
||||
QuickConnectDto(secret = state.secret),
|
||||
)
|
||||
serverRepository.changeUser(server.url, authenticationResult)
|
||||
withContext(Dispatchers.Main) {
|
||||
navigationManager.goToHome()
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error during quick connect")
|
||||
if (ex is InvalidStatusException && ex.status == 401) {
|
||||
withContext(Dispatchers.Main) {
|
||||
quickConnectState.value = null
|
||||
serverQuickConnect.value =
|
||||
serverQuickConnect.value!!.toMutableMap().apply {
|
||||
put(server.id, false)
|
||||
}
|
||||
quickConnectState.value = state
|
||||
}
|
||||
|
||||
while (!state.authenticated) {
|
||||
delay(5_000L)
|
||||
state =
|
||||
api.quickConnectApi
|
||||
.getQuickConnectState(
|
||||
secret = state.secret,
|
||||
).content
|
||||
withContext(Dispatchers.Main) {
|
||||
quickConnectState.value = state
|
||||
}
|
||||
}
|
||||
val authenticationResult by api.userApi.authenticateWithQuickConnect(
|
||||
QuickConnectDto(secret = state.secret),
|
||||
)
|
||||
serverRepository.changeUser(server.url, authenticationResult)
|
||||
withContext(Dispatchers.Main) {
|
||||
navigationManager.goToHome()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error during quick connect")
|
||||
if (ex is InvalidStatusException && ex.status == 401) {
|
||||
withContext(Dispatchers.Main) {
|
||||
quickConnectState.value = null
|
||||
serverQuickConnect.value =
|
||||
serverQuickConnect.value!!.toMutableMap().apply {
|
||||
put(server.id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
setError("Error with Quick Connect", ex)
|
||||
}
|
||||
setError("Error with Quick Connect", ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelQuickConnect() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue