mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-07 16:06:35 +01:00
Display server error message on server list (#1741)
## Description On the switch server page, display error message at the bottom for the focused server. This includes unsupported server version. ### Related issues Jellyfin 10.11/12.0 support discussion: #1640 ### Testing Emulator ## Screenshots <img width="1280" height="720" alt="server_error Large" src="https://github.com/user-attachments/assets/1227e7e7-a364-48d9-9a77-cb3f78d134a1" /> ## AI or LLM usage None
This commit is contained in:
parent
4171d0b861
commit
e7b0be2660
7 changed files with 200 additions and 34 deletions
|
|
@ -15,6 +15,8 @@ import org.acra.ReportField
|
||||||
import org.acra.config.dialog
|
import org.acra.config.dialog
|
||||||
import org.acra.data.StringFormat
|
import org.acra.data.StringFormat
|
||||||
import org.acra.ktx.initAcra
|
import org.acra.ktx.initAcra
|
||||||
|
import org.jellyfin.sdk.Jellyfin
|
||||||
|
import org.jellyfin.sdk.model.ServerVersion
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -120,5 +122,7 @@ class WholphinApplication :
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: WholphinApplication
|
lateinit var instance: WholphinApplication
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
val minimumServerVersion: ServerVersion = Jellyfin.minimumVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ fun rememberIdColor(
|
||||||
@Composable
|
@Composable
|
||||||
fun ServerIconCard(
|
fun ServerIconCard(
|
||||||
server: JellyfinServer,
|
server: JellyfinServer,
|
||||||
|
serverVersionSupported: ServerVersionSupported,
|
||||||
connectionStatus: ServerConnectionStatus,
|
connectionStatus: ServerConnectionStatus,
|
||||||
isCurrentServer: Boolean,
|
isCurrentServer: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
|
@ -166,28 +167,8 @@ fun ServerIconCard(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
// Show connection status indicator or server name/letter
|
// Show connection status indicator or server name/letter
|
||||||
when (connectionStatus) {
|
when {
|
||||||
is ServerConnectionStatus.Success -> {
|
connectionStatus is ServerConnectionStatus.Error || serverVersionSupported != ServerVersionSupported.SUPPORTED -> {
|
||||||
// Show server name/letter
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = displayText,
|
|
||||||
style =
|
|
||||||
MaterialTheme.typography.displayLarge.copy(
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
),
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerConnectionStatus.Pending -> {
|
|
||||||
CircularProgress(
|
|
||||||
modifier = Modifier.size(cardSize * 0.4f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ServerConnectionStatus.Error -> {
|
|
||||||
// Show warning icon with server letter below
|
// Show warning icon with server letter below
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
|
@ -195,7 +176,7 @@ fun ServerIconCard(
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Warning,
|
imageVector = Icons.Default.Warning,
|
||||||
contentDescription = connectionStatus.message,
|
contentDescription = (connectionStatus as? ServerConnectionStatus.Error)?.message,
|
||||||
tint = MaterialTheme.colorScheme.errorContainer,
|
tint = MaterialTheme.colorScheme.errorContainer,
|
||||||
modifier = Modifier.size(cardSize * 0.3f),
|
modifier = Modifier.size(cardSize * 0.3f),
|
||||||
)
|
)
|
||||||
|
|
@ -210,6 +191,26 @@ fun ServerIconCard(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectionStatus is ServerConnectionStatus.Success -> {
|
||||||
|
// Show server name/letter
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = displayText,
|
||||||
|
style =
|
||||||
|
MaterialTheme.typography.displayLarge.copy(
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionStatus == ServerConnectionStatus.Pending -> {
|
||||||
|
CircularProgress(
|
||||||
|
modifier = Modifier.size(cardSize * 0.4f),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
package com.github.damontecres.wholphin.ui.setup
|
package com.github.damontecres.wholphin.ui.setup
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -30,12 +35,16 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalResources
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
|
@ -55,6 +64,7 @@ import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
|
||||||
|
|
@ -85,8 +95,10 @@ private fun SwitchServerContentInternal(
|
||||||
viewModel: SwitchServerViewModel,
|
viewModel: SwitchServerViewModel,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val resources = LocalResources.current
|
||||||
var showAddServer by remember { mutableStateOf(false) }
|
var showAddServer by remember { mutableStateOf(false) }
|
||||||
var showDeleteDialog by remember { mutableStateOf<JellyfinServer?>(null) }
|
var showDeleteDialog by remember { mutableStateOf<JellyfinServer?>(null) }
|
||||||
|
var focusedIndex by rememberInt(0)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.dimAndBlur(showAddServer || showDeleteDialog != null),
|
modifier = modifier.dimAndBlur(showAddServer || showDeleteDialog != null),
|
||||||
) {
|
) {
|
||||||
|
|
@ -125,8 +137,13 @@ private fun SwitchServerContentInternal(
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
val firstServerFocus = remember { FocusRequester() }
|
val firstServerFocus = remember { FocusRequester() }
|
||||||
if (state.servers.isNotEmpty()) {
|
LaunchedEffect(state.loading) {
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
val state = state
|
||||||
|
if (state.loading == LoadingState.Success && state.servers.isNotEmpty()) {
|
||||||
|
firstServerFocus.tryRequestFocus()
|
||||||
|
} else if (state.loading == LoadingState.Pending) {
|
||||||
|
firstServerFocus.tryRequestFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LazyRow(
|
LazyRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
|
|
@ -141,6 +158,7 @@ private fun SwitchServerContentInternal(
|
||||||
ServerIconCard(
|
ServerIconCard(
|
||||||
server = server.server,
|
server = server.server,
|
||||||
connectionStatus = server.status,
|
connectionStatus = server.status,
|
||||||
|
serverVersionSupported = server.versionSupported,
|
||||||
isCurrentServer = false, // TODO: Determine current server if needed
|
isCurrentServer = false, // TODO: Determine current server if needed
|
||||||
onClick = {
|
onClick = {
|
||||||
when (server.status) {
|
when (server.status) {
|
||||||
|
|
@ -162,10 +180,13 @@ private fun SwitchServerContentInternal(
|
||||||
},
|
},
|
||||||
allowDelete = true,
|
allowDelete = true,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.ifElse(
|
Modifier
|
||||||
index == 0,
|
.onFocusChanged {
|
||||||
Modifier.focusRequester(firstServerFocus),
|
if (it.isFocused) focusedIndex = index
|
||||||
),
|
}.ifElse(
|
||||||
|
index == 0,
|
||||||
|
Modifier.focusRequester(firstServerFocus),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Add Server card - always rightmost
|
// Add Server card - always rightmost
|
||||||
|
|
@ -173,10 +194,13 @@ private fun SwitchServerContentInternal(
|
||||||
AddServerCard(
|
AddServerCard(
|
||||||
onClick = { showAddServer = true },
|
onClick = { showAddServer = true },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.ifElse(
|
Modifier
|
||||||
state.servers.isEmpty(),
|
.onFocusChanged {
|
||||||
Modifier.focusRequester(firstServerFocus),
|
if (it.isFocused) focusedIndex = -1
|
||||||
),
|
}.ifElse(
|
||||||
|
state.servers.isEmpty(),
|
||||||
|
Modifier.focusRequester(firstServerFocus),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +215,37 @@ private fun SwitchServerContentInternal(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val errorMessage =
|
||||||
|
remember(resources, focusedIndex, state.servers) {
|
||||||
|
val server = state.servers.getOrNull(focusedIndex)
|
||||||
|
serverErrorMessage(server, resources)
|
||||||
|
}
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = errorMessage,
|
||||||
|
transitionSpec = {
|
||||||
|
slideInVertically { it / 2 } togetherWith slideOutVertically { it }
|
||||||
|
},
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.BottomCenter),
|
||||||
|
) { message ->
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = message ?: "",
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 32.dp, end = 32.dp, bottom = 32.dp)
|
||||||
|
.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Delete server dialog
|
// Delete server dialog
|
||||||
showDeleteDialog?.let { server ->
|
showDeleteDialog?.let { server ->
|
||||||
DialogPopup(
|
DialogPopup(
|
||||||
|
|
@ -411,3 +466,26 @@ private fun SwitchServerContentInternal(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun serverErrorMessage(
|
||||||
|
server: ServerState?,
|
||||||
|
resources: Resources,
|
||||||
|
): String? =
|
||||||
|
when {
|
||||||
|
server?.status is ServerConnectionStatus.Error -> {
|
||||||
|
server.status.message
|
||||||
|
}
|
||||||
|
|
||||||
|
server?.status is ServerConnectionStatus.Success &&
|
||||||
|
server.versionSupported == ServerVersionSupported.NOT_SUPPORTED -> {
|
||||||
|
resources.getString(R.string.server_version_not_supported) + ": ${server.status.systemInfo.version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
server?.versionSupported == ServerVersionSupported.NOT_SUPPORTED -> {
|
||||||
|
resources.getString(R.string.server_version_not_supported)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import android.widget.Toast
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.WholphinApplication
|
||||||
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
|
|
@ -24,6 +25,7 @@ import org.jellyfin.sdk.api.client.HttpClientOptions
|
||||||
import org.jellyfin.sdk.api.client.extensions.systemApi
|
import org.jellyfin.sdk.api.client.extensions.systemApi
|
||||||
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
|
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
|
||||||
import org.jellyfin.sdk.discovery.RecommendedServerIssue
|
import org.jellyfin.sdk.discovery.RecommendedServerIssue
|
||||||
|
import org.jellyfin.sdk.model.ServerVersion
|
||||||
import org.jellyfin.sdk.model.serializer.toUUID
|
import org.jellyfin.sdk.model.serializer.toUUID
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -73,11 +75,30 @@ class SwitchServerViewModel
|
||||||
server: JellyfinServer,
|
server: JellyfinServer,
|
||||||
result: ServerConnectionStatus,
|
result: ServerConnectionStatus,
|
||||||
) {
|
) {
|
||||||
|
val supported =
|
||||||
|
if (result is ServerConnectionStatus.Success) {
|
||||||
|
val serverVersion =
|
||||||
|
result.systemInfo.version?.let { ServerVersion.fromString(it) }
|
||||||
|
if (serverVersion == null || serverVersion < WholphinApplication.minimumServerVersion) {
|
||||||
|
ServerVersionSupported.NOT_SUPPORTED
|
||||||
|
} else {
|
||||||
|
ServerVersionSupported.SUPPORTED
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ServerVersionSupported.UNKNOWN
|
||||||
|
}
|
||||||
_state.update {
|
_state.update {
|
||||||
val servers =
|
val servers =
|
||||||
it.servers.toMutableList().apply {
|
it.servers.toMutableList().apply {
|
||||||
val index = indexOfFirst { it.server.id == server.id }
|
val index = indexOfFirst { it.server.id == server.id }
|
||||||
set(index, get(index).copy(server = server, status = result))
|
set(
|
||||||
|
index,
|
||||||
|
get(index).copy(
|
||||||
|
server = server,
|
||||||
|
status = result,
|
||||||
|
versionSupported = supported,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
it.copy(servers = servers)
|
it.copy(servers = servers)
|
||||||
}
|
}
|
||||||
|
|
@ -245,4 +266,11 @@ data class ServerState(
|
||||||
val server: JellyfinServer,
|
val server: JellyfinServer,
|
||||||
val status: ServerConnectionStatus = ServerConnectionStatus.Pending,
|
val status: ServerConnectionStatus = ServerConnectionStatus.Pending,
|
||||||
val quickConnect: Boolean = false,
|
val quickConnect: Boolean = false,
|
||||||
|
val versionSupported: ServerVersionSupported = ServerVersionSupported.UNKNOWN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enum class ServerVersionSupported {
|
||||||
|
SUPPORTED,
|
||||||
|
NOT_SUPPORTED,
|
||||||
|
UNKNOWN,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,13 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalResources
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
|
@ -169,6 +171,37 @@ fun SwitchUserContent(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
when (state.serverVersionSupported) {
|
||||||
|
ServerVersionSupported.SUPPORTED -> {}
|
||||||
|
|
||||||
|
ServerVersionSupported.NOT_SUPPORTED,
|
||||||
|
ServerVersionSupported.UNKNOWN,
|
||||||
|
-> {
|
||||||
|
val resources = LocalResources.current
|
||||||
|
val message =
|
||||||
|
remember(resources) {
|
||||||
|
if (state.serverVersion.isNotNullOrBlank()) {
|
||||||
|
resources.getString(R.string.server_version_not_supported) + ": ${state.serverVersion}"
|
||||||
|
} else {
|
||||||
|
resources.getString(R.string.server_version_not_supported) + ": " +
|
||||||
|
resources.getString(R.string.unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = message,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 32.dp, end = 32.dp, bottom = 32.dp)
|
||||||
|
.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.ui.setup
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.github.damontecres.wholphin.WholphinApplication
|
||||||
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
|
|
@ -33,7 +34,9 @@ import org.jellyfin.sdk.api.client.exception.InvalidStatusException
|
||||||
import org.jellyfin.sdk.api.client.extensions.authenticateUserByName
|
import org.jellyfin.sdk.api.client.extensions.authenticateUserByName
|
||||||
import org.jellyfin.sdk.api.client.extensions.imageApi
|
import org.jellyfin.sdk.api.client.extensions.imageApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.quickConnectApi
|
import org.jellyfin.sdk.api.client.extensions.quickConnectApi
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.systemApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.userApi
|
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||||
|
import org.jellyfin.sdk.model.ServerVersion
|
||||||
import org.jellyfin.sdk.model.api.QuickConnectDto
|
import org.jellyfin.sdk.model.api.QuickConnectDto
|
||||||
import org.jellyfin.sdk.model.api.QuickConnectResult
|
import org.jellyfin.sdk.model.api.QuickConnectResult
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -78,10 +81,13 @@ class SwitchUserViewModel
|
||||||
_state.update { SwitchUserState() }
|
_state.update { SwitchUserState() }
|
||||||
try {
|
try {
|
||||||
val serverUsers = getUsers()
|
val serverUsers = getUsers()
|
||||||
|
val (serverVersion, supported) = checkServerVersion()
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
loading = LoadingState.Success,
|
loading = LoadingState.Success,
|
||||||
users = serverUsers,
|
users = serverUsers,
|
||||||
|
serverVersion = serverVersion,
|
||||||
|
serverVersionSupported = supported,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
|
@ -272,6 +278,19 @@ class SwitchUserViewModel
|
||||||
knownUsers + publicUsers
|
knownUsers + publicUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun checkServerVersion(): Pair<String?, ServerVersionSupported> {
|
||||||
|
val api = jellyfin.createApi(server.url)
|
||||||
|
val systemInfo by api.systemApi.getPublicSystemInfo()
|
||||||
|
val serverVersion = systemInfo.version?.let { ServerVersion.fromString(it) }
|
||||||
|
return if (serverVersion == null) {
|
||||||
|
systemInfo.version to ServerVersionSupported.UNKNOWN
|
||||||
|
} else if (serverVersion < WholphinApplication.minimumServerVersion) {
|
||||||
|
systemInfo.version to ServerVersionSupported.NOT_SUPPORTED
|
||||||
|
} else {
|
||||||
|
systemInfo.version to ServerVersionSupported.SUPPORTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setError(
|
private fun setError(
|
||||||
msg: String? = null,
|
msg: String? = null,
|
||||||
ex: Exception? = null,
|
ex: Exception? = null,
|
||||||
|
|
@ -300,4 +319,6 @@ data class SwitchUserState(
|
||||||
// LoadingState for while adding/switching users
|
// LoadingState for while adding/switching users
|
||||||
val switchUserState: LoadingState = LoadingState.Pending,
|
val switchUserState: LoadingState = LoadingState.Pending,
|
||||||
val loginAttempts: Int = 0,
|
val loginAttempts: Int = 0,
|
||||||
|
val serverVersion: String? = null,
|
||||||
|
val serverVersionSupported: ServerVersionSupported = ServerVersionSupported.UNKNOWN,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -898,4 +898,5 @@
|
||||||
<string name="user_views">User views</string>
|
<string name="user_views">User views</string>
|
||||||
<string name="books">Books</string>
|
<string name="books">Books</string>
|
||||||
<string name="audio_books">Audio books</string>
|
<string name="audio_books">Audio books</string>
|
||||||
|
<string name="server_version_not_supported">Server version not supported</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue