mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Fix some login issues (#23)
Fixes #21 Might also address #18 Fixes an issue with logging using username/password due to network access on wrong thread exceptions. Also adds better handling when deleting users or servers, especially if it's the current one.
This commit is contained in:
parent
0e195c18d7
commit
bd4d4d5092
4 changed files with 72 additions and 35 deletions
|
|
@ -11,6 +11,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
|
@ -118,6 +119,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
key(server, user) {
|
||||||
val initialDestination =
|
val initialDestination =
|
||||||
if (server != null && user != null) {
|
if (server != null && user != null) {
|
||||||
Destination.Home()
|
Destination.Home()
|
||||||
|
|
@ -150,3 +152,4 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class ServerRepository
|
||||||
apiClient.update(baseUrl = server.url, accessToken = null)
|
apiClient.update(baseUrl = server.url, accessToken = null)
|
||||||
_currentServer = server
|
_currentServer = server
|
||||||
_currentUser = null
|
_currentUser = null
|
||||||
|
_currentUserDto = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -160,6 +161,7 @@ class ServerRepository
|
||||||
suspend fun removeUser(user: JellyfinUser) {
|
suspend fun removeUser(user: JellyfinUser) {
|
||||||
if (currentUser == user) {
|
if (currentUser == user) {
|
||||||
_currentUser = null
|
_currentUser = null
|
||||||
|
_currentUserDto = null
|
||||||
userPreferencesDataStore.updateData {
|
userPreferencesDataStore.updateData {
|
||||||
it
|
it
|
||||||
.toBuilder()
|
.toBuilder()
|
||||||
|
|
@ -177,13 +179,17 @@ class ServerRepository
|
||||||
suspend fun removeServer(server: JellyfinServer) {
|
suspend fun removeServer(server: JellyfinServer) {
|
||||||
if (currentServer == server) {
|
if (currentServer == server) {
|
||||||
_currentServer = null
|
_currentServer = null
|
||||||
|
_currentUser = null
|
||||||
|
_currentUserDto = null
|
||||||
userPreferencesDataStore.updateData {
|
userPreferencesDataStore.updateData {
|
||||||
it
|
it
|
||||||
.toBuilder()
|
.toBuilder()
|
||||||
.apply {
|
.apply {
|
||||||
currentServerId = ""
|
currentServerId = ""
|
||||||
|
currentUserId = ""
|
||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
|
apiClient.update(baseUrl = null, accessToken = null)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
serverDao.deleteServer(server.id)
|
serverDao.deleteServer(server.id)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,12 @@ import androidx.media3.common.Player
|
||||||
import coil3.request.ErrorResult
|
import coil3.request.ErrorResult
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumnSaver
|
import com.github.damontecres.wholphin.ui.data.RowColumnSaver
|
||||||
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
|
|
@ -39,6 +43,7 @@ import timber.log.Timber
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
@ -337,3 +342,12 @@ suspend fun showToast(
|
||||||
) = withContext(Dispatchers.Main) {
|
) = withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches a coroutine with [Dispatchers.IO] plus the provided [CoroutineContext] defaulting to using [ExceptionHandler]
|
||||||
|
*/
|
||||||
|
fun CoroutineScope.launchIO(
|
||||||
|
context: CoroutineContext = ExceptionHandler(),
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
|
block: suspend CoroutineScope.() -> Unit,
|
||||||
|
): Job = launch(context = Dispatchers.IO + context, start = start, block = block)
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,14 @@ import com.github.damontecres.wholphin.data.JellyfinServer
|
||||||
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
||||||
import com.github.damontecres.wholphin.data.JellyfinUser
|
import com.github.damontecres.wholphin.data.JellyfinUser
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.Jellyfin
|
import org.jellyfin.sdk.Jellyfin
|
||||||
import org.jellyfin.sdk.api.client.HttpClientOptions
|
import org.jellyfin.sdk.api.client.HttpClientOptions
|
||||||
|
|
@ -62,7 +61,11 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
val allServers =
|
val allServers =
|
||||||
serverDao
|
serverDao
|
||||||
.getServers()
|
.getServers()
|
||||||
|
|
@ -116,7 +119,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launchIO {
|
||||||
serverRepository.currentServer?.let {
|
serverRepository.currentServer?.let {
|
||||||
val quickConnect =
|
val quickConnect =
|
||||||
jellyfin
|
jellyfin
|
||||||
|
|
@ -140,11 +143,14 @@ class SwitchUserViewModel
|
||||||
server: JellyfinServer,
|
server: JellyfinServer,
|
||||||
user: JellyfinUser,
|
user: JellyfinUser,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(ExceptionHandler()) {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
serverRepository.changeUser(server, user)
|
serverRepository.changeUser(server, user)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
navigationManager.goToHome()
|
navigationManager.goToHome()
|
||||||
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error switching user")
|
||||||
switchUserState.value = LoadingState.Error(exception = ex)
|
switchUserState.value = LoadingState.Error(exception = ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +162,7 @@ class SwitchUserViewModel
|
||||||
password: String,
|
password: String,
|
||||||
) {
|
) {
|
||||||
quickConnectJob?.cancel()
|
quickConnectJob?.cancel()
|
||||||
viewModelScope.launch(ExceptionHandler()) {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
val api = jellyfin.createApi(baseUrl = server.url)
|
val api = jellyfin.createApi(baseUrl = server.url)
|
||||||
val authenticationResult by api.userApi.authenticateUserByName(
|
val authenticationResult by api.userApi.authenticateUserByName(
|
||||||
|
|
@ -168,6 +174,7 @@ class SwitchUserViewModel
|
||||||
navigationManager.goToHome()
|
navigationManager.goToHome()
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error logging in user")
|
||||||
switchUserState.value = LoadingState.Error(ex)
|
switchUserState.value = LoadingState.Error(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +185,7 @@ class SwitchUserViewModel
|
||||||
onAuthenticated: () -> Unit,
|
onAuthenticated: () -> Unit,
|
||||||
) {
|
) {
|
||||||
quickConnectJob?.cancel()
|
quickConnectJob?.cancel()
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
val api = jellyfin.createApi(server.url)
|
val api = jellyfin.createApi(server.url)
|
||||||
var state =
|
var state =
|
||||||
|
|
@ -191,7 +198,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
quickConnectJob =
|
quickConnectJob =
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launchIO {
|
||||||
while (!state.authenticated) {
|
while (!state.authenticated) {
|
||||||
delay(5_000L)
|
delay(5_000L)
|
||||||
state =
|
state =
|
||||||
|
|
@ -212,6 +219,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error during quick connect")
|
||||||
if (ex is InvalidStatusException && ex.status == 401) {
|
if (ex is InvalidStatusException && ex.status == 401) {
|
||||||
quickConnectState.value = null
|
quickConnectState.value = null
|
||||||
serverQuickConnect.value =
|
serverQuickConnect.value =
|
||||||
|
|
@ -231,7 +239,7 @@ class SwitchUserViewModel
|
||||||
|
|
||||||
fun addServer(serverUrl: String) {
|
fun addServer(serverUrl: String) {
|
||||||
addServerState.value = LoadingState.Loading
|
addServerState.value = LoadingState.Loading
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
val serverInfo by jellyfin
|
val serverInfo by jellyfin
|
||||||
.createApi(serverUrl)
|
.createApi(serverUrl)
|
||||||
|
|
@ -279,19 +287,25 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUser(user: JellyfinUser) {
|
fun removeUser(user: JellyfinUser) {
|
||||||
viewModelScope.launch(ExceptionHandler()) {
|
viewModelScope.launchIO {
|
||||||
serverRepository.removeUser(user)
|
serverRepository.removeUser(user)
|
||||||
|
val serverUsers =
|
||||||
|
serverDao.getServer(user.serverId)?.users?.sortedBy { it.name } ?: listOf()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
users.value = serverUsers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeServer(server: JellyfinServer) {
|
fun removeServer(server: JellyfinServer) {
|
||||||
viewModelScope.launch(ExceptionHandler()) {
|
viewModelScope.launchIO {
|
||||||
serverRepository.removeServer(server)
|
serverRepository.removeServer(server)
|
||||||
|
init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun discoverServers() {
|
fun discoverServers() {
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launchIO {
|
||||||
jellyfin.discovery.discoverLocalServers().collect { server ->
|
jellyfin.discovery.discoverLocalServers().collect { server ->
|
||||||
val newServerList =
|
val newServerList =
|
||||||
discoveredServers.value!!
|
discoveredServers.value!!
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue