mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +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.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
|
@ -118,31 +119,33 @@ class MainActivity : AppCompatActivity() {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
val initialDestination =
|
||||
if (server != null && user != null) {
|
||||
Destination.Home()
|
||||
} else {
|
||||
Destination.ServerList
|
||||
}
|
||||
val backStack = rememberNavBackStack(initialDestination)
|
||||
navigationManager.backStack = backStack
|
||||
if (appPreferences.autoCheckForUpdates) {
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
updateChecker.maybeShowUpdateToast(appPreferences.updateUrl)
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(ex, "Failed to check for update")
|
||||
key(server, user) {
|
||||
val initialDestination =
|
||||
if (server != null && user != null) {
|
||||
Destination.Home()
|
||||
} else {
|
||||
Destination.ServerList
|
||||
}
|
||||
val backStack = rememberNavBackStack(initialDestination)
|
||||
navigationManager.backStack = backStack
|
||||
if (appPreferences.autoCheckForUpdates) {
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
updateChecker.maybeShowUpdateToast(appPreferences.updateUrl)
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(ex, "Failed to check for update")
|
||||
}
|
||||
}
|
||||
}
|
||||
ApplicationContent(
|
||||
user = user,
|
||||
server = server,
|
||||
navigationManager = navigationManager,
|
||||
preferences = preferences,
|
||||
deviceProfile = deviceProfile,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
ApplicationContent(
|
||||
user = user,
|
||||
server = server,
|
||||
navigationManager = navigationManager,
|
||||
preferences = preferences,
|
||||
deviceProfile = deviceProfile,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class ServerRepository
|
|||
apiClient.update(baseUrl = server.url, accessToken = null)
|
||||
_currentServer = server
|
||||
_currentUser = null
|
||||
_currentUserDto = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,6 +161,7 @@ class ServerRepository
|
|||
suspend fun removeUser(user: JellyfinUser) {
|
||||
if (currentUser == user) {
|
||||
_currentUser = null
|
||||
_currentUserDto = null
|
||||
userPreferencesDataStore.updateData {
|
||||
it
|
||||
.toBuilder()
|
||||
|
|
@ -177,13 +179,17 @@ class ServerRepository
|
|||
suspend fun removeServer(server: JellyfinServer) {
|
||||
if (currentServer == server) {
|
||||
_currentServer = null
|
||||
_currentUser = null
|
||||
_currentUserDto = null
|
||||
userPreferencesDataStore.updateData {
|
||||
it
|
||||
.toBuilder()
|
||||
.apply {
|
||||
currentServerId = ""
|
||||
currentUserId = ""
|
||||
}.build()
|
||||
}
|
||||
apiClient.update(baseUrl = null, accessToken = null)
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
serverDao.deleteServer(server.id)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,12 @@ import androidx.media3.common.Player
|
|||
import coil3.request.ErrorResult
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumnSaver
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
|
|
@ -39,6 +43,7 @@ import timber.log.Timber
|
|||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.min
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
|
@ -337,3 +342,12 @@ suspend fun showToast(
|
|||
) = withContext(Dispatchers.Main) {
|
||||
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.JellyfinUser
|
||||
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.NavigationManager
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.Jellyfin
|
||||
import org.jellyfin.sdk.api.client.HttpClientOptions
|
||||
|
|
@ -62,7 +61,11 @@ class SwitchUserViewModel
|
|||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
init()
|
||||
}
|
||||
|
||||
fun init() {
|
||||
viewModelScope.launchIO {
|
||||
val allServers =
|
||||
serverDao
|
||||
.getServers()
|
||||
|
|
@ -116,7 +119,7 @@ class SwitchUserViewModel
|
|||
}
|
||||
}
|
||||
}
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
serverRepository.currentServer?.let {
|
||||
val quickConnect =
|
||||
jellyfin
|
||||
|
|
@ -140,11 +143,14 @@ class SwitchUserViewModel
|
|||
server: JellyfinServer,
|
||||
user: JellyfinUser,
|
||||
) {
|
||||
viewModelScope.launch(ExceptionHandler()) {
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
serverRepository.changeUser(server, user)
|
||||
navigationManager.goToHome()
|
||||
withContext(Dispatchers.Main) {
|
||||
navigationManager.goToHome()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error switching user")
|
||||
switchUserState.value = LoadingState.Error(exception = ex)
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +162,7 @@ class SwitchUserViewModel
|
|||
password: String,
|
||||
) {
|
||||
quickConnectJob?.cancel()
|
||||
viewModelScope.launch(ExceptionHandler()) {
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
val api = jellyfin.createApi(baseUrl = server.url)
|
||||
val authenticationResult by api.userApi.authenticateUserByName(
|
||||
|
|
@ -168,6 +174,7 @@ class SwitchUserViewModel
|
|||
navigationManager.goToHome()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error logging in user")
|
||||
switchUserState.value = LoadingState.Error(ex)
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +185,7 @@ class SwitchUserViewModel
|
|||
onAuthenticated: () -> Unit,
|
||||
) {
|
||||
quickConnectJob?.cancel()
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
val api = jellyfin.createApi(server.url)
|
||||
var state =
|
||||
|
|
@ -191,7 +198,7 @@ class SwitchUserViewModel
|
|||
}
|
||||
|
||||
quickConnectJob =
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
while (!state.authenticated) {
|
||||
delay(5_000L)
|
||||
state =
|
||||
|
|
@ -212,6 +219,7 @@ class SwitchUserViewModel
|
|||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error during quick connect")
|
||||
if (ex is InvalidStatusException && ex.status == 401) {
|
||||
quickConnectState.value = null
|
||||
serverQuickConnect.value =
|
||||
|
|
@ -231,7 +239,7 @@ class SwitchUserViewModel
|
|||
|
||||
fun addServer(serverUrl: String) {
|
||||
addServerState.value = LoadingState.Loading
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
val serverInfo by jellyfin
|
||||
.createApi(serverUrl)
|
||||
|
|
@ -279,19 +287,25 @@ class SwitchUserViewModel
|
|||
}
|
||||
|
||||
fun removeUser(user: JellyfinUser) {
|
||||
viewModelScope.launch(ExceptionHandler()) {
|
||||
viewModelScope.launchIO {
|
||||
serverRepository.removeUser(user)
|
||||
val serverUsers =
|
||||
serverDao.getServer(user.serverId)?.users?.sortedBy { it.name } ?: listOf()
|
||||
withContext(Dispatchers.Main) {
|
||||
users.value = serverUsers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeServer(server: JellyfinServer) {
|
||||
viewModelScope.launch(ExceptionHandler()) {
|
||||
viewModelScope.launchIO {
|
||||
serverRepository.removeServer(server)
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
fun discoverServers() {
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
jellyfin.discovery.discoverLocalServers().collect { server ->
|
||||
val newServerList =
|
||||
discoveredServers.value!!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue