Fixes for Jellyseerr login issues (#1031)

## Description
Fixes some issues with Jellyseerr login. Also makes error message more
clear by showing the URLs that were tested and the error that occurred.

Also fixes an issue where you could not remove a Jellyseerr server if it
was previously configured but can no longer connect.

### Related issues
Fixes #1028
Should fix #1022
Related to #747

### Testing
Emulator, tested adding and removing valid & invalid IPs

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Ray 2026-03-04 15:11:09 -05:00 • committed by GitHub
parent 6867ba7657
commit ce6461d23b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 165 additions and 96 deletions

View file

@ -554,6 +554,7 @@ fun PreferencesContent(
currentUsername = currentUser?.name,
status = status,
onSubmit = seerrVm::submitServer,
onResetStatus = seerrVm::resetStatus,
onDismissRequest = { seerrDialogMode = SeerrDialogMode.None },
)
}

View file

@ -4,7 +4,6 @@ import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@ -66,15 +65,19 @@ fun AddSeerrServerApiKey(
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.align(Alignment.CenterHorizontally),
)
val labelWidth = 90.dp
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.CenterHorizontally),
) {
Text(
text = stringResource(R.string.url),
modifier = Modifier.padding(end = 8.dp),
modifier =
Modifier
.width(labelWidth)
.padding(end = 8.dp),
)
EditTextBox(
value = url,
@ -105,7 +108,10 @@ fun AddSeerrServerApiKey(
) {
Text(
text = stringResource(R.string.api_key),
modifier = Modifier.padding(end = 8.dp),
modifier =
Modifier
.width(labelWidth)
.padding(end = 8.dp),
)
EditTextBox(
value = apiKey,
@ -173,7 +179,7 @@ fun AddSeerrServerUsername(
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.align(Alignment.CenterHorizontally),
)
val labelWidth = 90.dp
Row(

View file

@ -1,12 +1,16 @@
package com.github.damontecres.wholphin.ui.setup.seerr
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
import com.github.damontecres.wholphin.ui.components.BasicDialog
@ -20,6 +24,7 @@ fun AddSeerServerDialog(
currentUsername: String?,
status: LoadingState,
onSubmit: (url: String, username: String, passwordOrApiKey: String, method: SeerrAuthMethod) -> Unit,
onResetStatus: () -> Unit,
onDismissRequest: () -> Unit,
) {
var authMethod by remember { mutableStateOf<SeerrAuthMethod?>(null) }
@ -34,6 +39,7 @@ fun AddSeerServerDialog(
-> {
BasicDialog(
onDismissRequest = { authMethod = null },
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
AddSeerrServerUsername(
onSubmit = { url, username, password ->
@ -41,6 +47,7 @@ fun AddSeerServerDialog(
},
username = currentUsername ?: "",
status = status,
modifier = Modifier.widthIn(min = 320.dp),
)
}
}
@ -54,6 +61,7 @@ fun AddSeerServerDialog(
onSubmit.invoke(url, "", apiKey, SeerrAuthMethod.API_KEY)
},
status = status,
modifier = Modifier.widthIn(min = 320.dp),
)
}
}
@ -61,7 +69,10 @@ fun AddSeerServerDialog(
null -> {
ChooseSeerrLoginType(
onDismissRequest = onDismissRequest,
onChoose = { authMethod = it },
onChoose = {
onResetStatus.invoke()
authMethod = it
},
)
}
}

View file

@ -50,63 +50,73 @@ class SwitchSeerrViewModel
serverConnectionStatus.update { LoadingState.Error("Invalid URL", ex) }
return@launchIO
}
var result: LoadingState = LoadingState.Error("No url")
Timber.v("Urls to try: %s", urls)
val results = mutableMapOf<HttpUrl, LoadingState>()
for (url in urls) {
Timber.d("Trying %s", url)
result =
try {
seerrServerRepository.testConnection(
authMethod = authMethod,
url = url.toString(),
username = username.takeIf { authMethod != SeerrAuthMethod.API_KEY },
passwordOrApiKey = passwordOrApiKey,
)
} catch (ex: ClientException) {
Timber.w(ex, "ClientException logging in")
if (ex.statusCode == 401 || ex.statusCode == 403) {
showToast(context, "Invalid credentials")
result = LoadingState.Error("Invalid credentials", ex)
break
} else {
LoadingState.Error("Could not connect with URL")
}
} catch (ex: Exception) {
Timber.w(ex, "Exception logging in")
LoadingState.Error(ex)
}
if (result is LoadingState.Success) {
when (authMethod) {
SeerrAuthMethod.LOCAL,
SeerrAuthMethod.JELLYFIN,
-> {
seerrServerRepository.addAndChangeServer(
url.toString(),
authMethod,
username,
passwordOrApiKey,
)
}
SeerrAuthMethod.API_KEY -> {
seerrServerRepository.addAndChangeServer(
url.toString(),
passwordOrApiKey,
)
}
}
try {
seerrServerRepository.testConnection(
authMethod = authMethod,
url = url.toString(),
username = username.takeIf { authMethod != SeerrAuthMethod.API_KEY },
passwordOrApiKey = passwordOrApiKey,
)
results[url] = LoadingState.Success
break
} catch (ex: ClientException) {
Timber.w(ex, "ClientException logging in %s", url)
if (ex.statusCode == 401 || ex.statusCode == 403) {
showToast(context, "Invalid credentials")
results[url] = LoadingState.Error("Invalid credentials", ex)
} else {
results[url] = LoadingState.Error("Could not connect with URL")
}
} catch (ex: Exception) {
Timber.w(ex, "ClientException logging in %s", url)
results[url] = LoadingState.Error(ex)
}
}
if (result is LoadingState.Error) {
showToast(context, "Error: ${result.message}")
val result = results.filter { (url, state) -> state is LoadingState.Success }
if (result.isNotEmpty()) {
val url = result.keys.first()
when (authMethod) {
SeerrAuthMethod.LOCAL,
SeerrAuthMethod.JELLYFIN,
-> {
seerrServerRepository.addAndChangeServer(
url.toString(),
authMethod,
username,
passwordOrApiKey,
)
}
SeerrAuthMethod.API_KEY -> {
seerrServerRepository.addAndChangeServer(
url.toString(),
passwordOrApiKey,
)
}
}
} else {
val message =
results
.map { (url, state) ->
val s = state as? LoadingState.Error
"$url - ${s?.localizedMessage}"
}.joinToString("\n")
showToast(context, "Could not connect")
serverConnectionStatus.update { LoadingState.Error(message) }
}
serverConnectionStatus.update { result }
}
}
fun removeServer() {
viewModelScope.launchIO {
seerrServerRepository.removeServer()
val result = seerrServerRepository.removeServerForCurrentUser()
if (!result) {
showToast(context, "Could not remove server")
}
}
}