mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Add setting to toggle automatic sign in (#538)
## Description The major user facing change in this PR is adding back the setting to toggle automatic sign in on or off. This controls whether to restore the app to where you left off or go the user selection page. If enabled: re-opening the app will restore the previous user's session If disabled: re-opening the app will always start with the user selection page for the most recent server, or if there isn't a recent server, the server selection page This also separate this setting from the PIN protection settings in previous PRs. ### Dev changes This PR refactors the "setup" UI (server & user selection) to be separate from the "app" UI (the actually app content). There is now a `NavDisplay` for the setup flow which ends with displaying the `ApplicationContent`'s `NavDisplay`. Most of the logic is moved out of Compose code and into a `ViewModel`. This means to switch users/server, the `SetupNavigationManager` must be used instead of `NavigationManager`. ### Related issues Closes #396 Fixes #376
This commit is contained in:
parent
20fe5e2626
commit
eb4e9b93be
14 changed files with 302 additions and 198 deletions
|
|
@ -2,6 +2,7 @@ package com.github.damontecres.wholphin
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -12,18 +13,18 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||||
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.livedata.observeAsState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation3.runtime.rememberNavBackStack
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||||
|
import androidx.navigation3.runtime.NavEntry
|
||||||
|
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||||
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Surface
|
import androidx.tv.material3.Surface
|
||||||
|
|
@ -33,24 +34,30 @@ import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration
|
import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.AppUpgradeHandler
|
import com.github.damontecres.wholphin.services.AppUpgradeHandler
|
||||||
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.DeviceProfileService
|
import com.github.damontecres.wholphin.services.DeviceProfileService
|
||||||
import com.github.damontecres.wholphin.services.ImageUrlService
|
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.PlaybackLifecycleObserver
|
import com.github.damontecres.wholphin.services.PlaybackLifecycleObserver
|
||||||
import com.github.damontecres.wholphin.services.RefreshRateService
|
import com.github.damontecres.wholphin.services.RefreshRateService
|
||||||
import com.github.damontecres.wholphin.services.ServerEventListener
|
import com.github.damontecres.wholphin.services.SetupDestination
|
||||||
|
import com.github.damontecres.wholphin.services.SetupNavigationManager
|
||||||
import com.github.damontecres.wholphin.services.UpdateChecker
|
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
|
import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient
|
||||||
import com.github.damontecres.wholphin.ui.CoilConfig
|
import com.github.damontecres.wholphin.ui.CoilConfig
|
||||||
import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.ApplicationContent
|
import com.github.damontecres.wholphin.ui.nav.ApplicationContent
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.setup.SwitchServerContent
|
||||||
|
import com.github.damontecres.wholphin.ui.setup.SwitchUserContent
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
|
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
|
||||||
import com.github.damontecres.wholphin.util.DebugLogTree
|
import com.github.damontecres.wholphin.util.DebugLogTree
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
|
|
@ -59,8 +66,7 @@ import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
@Inject
|
private val viewModel: MainActivityViewModel by viewModels()
|
||||||
lateinit var serverRepository: ServerRepository
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var userPreferencesDataStore: DataStore<AppPreferences>
|
lateinit var userPreferencesDataStore: DataStore<AppPreferences>
|
||||||
|
|
@ -72,27 +78,26 @@ class MainActivity : AppCompatActivity() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var navigationManager: NavigationManager
|
lateinit var navigationManager: NavigationManager
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var setupNavigationManager: SetupNavigationManager
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var updateChecker: UpdateChecker
|
lateinit var updateChecker: UpdateChecker
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appUpgradeHandler: AppUpgradeHandler
|
lateinit var appUpgradeHandler: AppUpgradeHandler
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var serverEventListener: ServerEventListener
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var playbackLifecycleObserver: PlaybackLifecycleObserver
|
lateinit var playbackLifecycleObserver: PlaybackLifecycleObserver
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var deviceProfileService: DeviceProfileService
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var imageUrlService: ImageUrlService
|
lateinit var imageUrlService: ImageUrlService
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var refreshRateService: RefreshRateService
|
lateinit var refreshRateService: RefreshRateService
|
||||||
|
|
||||||
|
private var signInAuto = true
|
||||||
|
|
||||||
@OptIn(ExperimentalTvMaterial3Api::class)
|
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
@ -109,9 +114,13 @@ class MainActivity : AppCompatActivity() {
|
||||||
window.attributes = attrs.apply { preferredRefreshRate = mode.refreshRate }
|
window.attributes = attrs.apply { preferredRefreshRate = mode.refreshRate }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewModel.appStart()
|
||||||
setContent {
|
setContent {
|
||||||
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
val appPreferences by userPreferencesDataStore.data.collectAsState(null)
|
||||||
appPreferences?.let { appPreferences ->
|
appPreferences?.let { appPreferences ->
|
||||||
|
LaunchedEffect(appPreferences.signInAutomatically) {
|
||||||
|
signInAuto = appPreferences.signInAutomatically
|
||||||
|
}
|
||||||
CoilConfig(
|
CoilConfig(
|
||||||
diskCacheSizeBytes =
|
diskCacheSizeBytes =
|
||||||
appPreferences.advancedPreferences.imageDiskCacheSizeBytes.let {
|
appPreferences.advancedPreferences.imageDiskCacheSizeBytes.let {
|
||||||
|
|
@ -140,100 +149,71 @@ class MainActivity : AppCompatActivity() {
|
||||||
.background(MaterialTheme.colorScheme.background),
|
.background(MaterialTheme.colorScheme.background),
|
||||||
shape = RectangleShape,
|
shape = RectangleShape,
|
||||||
) {
|
) {
|
||||||
var isRestoringSession by remember { mutableStateOf(true) }
|
// val backStack = rememberNavBackStack(SetupDestination.Loading)
|
||||||
LaunchedEffect(Unit) {
|
// setupNavigationManager.backStack = backStack
|
||||||
// TODO PIN-related
|
val backStack = setupNavigationManager.backStack
|
||||||
// if (appPreferences.signInAutomatically) {
|
NavDisplay(
|
||||||
try {
|
backStack = backStack,
|
||||||
serverRepository.restoreSession(
|
onBack = { backStack.removeLastOrNull() },
|
||||||
appPreferences.currentServerId?.toUUIDOrNull(),
|
entryDecorators =
|
||||||
appPreferences.currentUserId?.toUUIDOrNull(),
|
listOf(
|
||||||
)
|
rememberSaveableStateHolderNavEntryDecorator(),
|
||||||
} catch (ex: Exception) {
|
rememberViewModelStoreNavEntryDecorator(),
|
||||||
Timber.e(ex, "Exception restoring session")
|
),
|
||||||
}
|
entryProvider = { key ->
|
||||||
// }
|
key as SetupDestination
|
||||||
isRestoringSession = false
|
NavEntry(key) {
|
||||||
}
|
when (key) {
|
||||||
val current by serverRepository.current.observeAsState()
|
SetupDestination.Loading -> {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(200.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = MaterialTheme.colorScheme.border,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val preferences =
|
SetupDestination.ServerList -> {
|
||||||
UserPreferences(
|
SwitchServerContent(Modifier.fillMaxSize())
|
||||||
appPreferences,
|
}
|
||||||
current?.userDto?.configuration ?: DefaultUserConfiguration,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isRestoringSession) {
|
is SetupDestination.UserList -> {
|
||||||
Box(
|
SwitchUserContent(
|
||||||
modifier = Modifier.size(200.dp),
|
currentServer = key.server,
|
||||||
contentAlignment = Alignment.Center,
|
Modifier.fillMaxSize(),
|
||||||
) {
|
)
|
||||||
CircularProgressIndicator(
|
}
|
||||||
color = MaterialTheme.colorScheme.border,
|
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO PIN-related
|
|
||||||
// DisposableEffect(Unit) {
|
|
||||||
// onDispose {
|
|
||||||
// if (!appPreferences.signInAutomatically || current?.user?.hasPin == true) {
|
|
||||||
// serverRepository.closeSession()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
key(current?.server?.id, current?.user?.id) {
|
|
||||||
// TODO PIN-related
|
|
||||||
// LaunchedEffect(current?.user?.pin) {
|
|
||||||
// if (current?.user?.pin?.isNotNullOrBlank() == true) {
|
|
||||||
// // If user has a pin, then obscure the window in previews
|
|
||||||
// window?.setFlags(
|
|
||||||
// WindowManager.LayoutParams.FLAG_SECURE,
|
|
||||||
// WindowManager.LayoutParams.FLAG_SECURE,
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
val initialDestination =
|
|
||||||
when {
|
|
||||||
current != null -> Destination.Home()
|
|
||||||
|
|
||||||
// TODO PIN-related
|
is SetupDestination.AppContent -> {
|
||||||
// !appPreferences.signInAutomatically -> Destination.ServerList
|
val current = key.current
|
||||||
|
val preferences =
|
||||||
else -> Destination.ServerList
|
UserPreferences(
|
||||||
}
|
appPreferences,
|
||||||
val backStack = rememberNavBackStack(initialDestination)
|
current.userDto.configuration
|
||||||
navigationManager.backStack = backStack
|
?: DefaultUserConfiguration,
|
||||||
if (UpdateChecker.ACTIVE && appPreferences.autoCheckForUpdates) {
|
)
|
||||||
LaunchedEffect(Unit) {
|
ProvideLocalClock {
|
||||||
try {
|
LaunchedEffect(Unit) {
|
||||||
updateChecker.maybeShowUpdateToast(appPreferences.updateUrl)
|
updateChecker.maybeShowUpdateToast(
|
||||||
} catch (ex: Exception) {
|
appPreferences.updateUrl,
|
||||||
Timber.w(ex, "Failed to check for update")
|
)
|
||||||
|
}
|
||||||
|
ApplicationContent(
|
||||||
|
user = current.user,
|
||||||
|
server = current.server,
|
||||||
|
navigationManager = navigationManager,
|
||||||
|
preferences = preferences,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(current, preferences) {
|
},
|
||||||
withContext(Dispatchers.IO) {
|
)
|
||||||
deviceProfileService.getOrCreateDeviceProfile(
|
|
||||||
preferences.appPreferences.playbackPreferences,
|
|
||||||
current?.server?.serverVersion,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProvideLocalClock {
|
|
||||||
ApplicationContent(
|
|
||||||
user = current?.user,
|
|
||||||
server = current?.server,
|
|
||||||
navigationManager = navigationManager,
|
|
||||||
preferences = preferences,
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -250,12 +230,69 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onRestart() {
|
override fun onRestart() {
|
||||||
super.onRestart()
|
super.onRestart()
|
||||||
// TODO PIN-related
|
Timber.i("onRestart")
|
||||||
|
viewModel.appStart()
|
||||||
// val signInAutomatically =
|
// val signInAutomatically =
|
||||||
// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true
|
// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true
|
||||||
// Timber.i("onRestart: signInAutomatically=$signInAutomatically")
|
|
||||||
// if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) {
|
// // TODO PIN-related
|
||||||
|
// // if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) {
|
||||||
|
// if (!signInAutomatically) {
|
||||||
// serverRepository.closeSession()
|
// serverRepository.closeSession()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class MainActivityViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val preferences: DataStore<AppPreferences>,
|
||||||
|
private val serverRepository: ServerRepository,
|
||||||
|
private val navigationManager: SetupNavigationManager,
|
||||||
|
private val deviceProfileService: DeviceProfileService,
|
||||||
|
private val backdropService: BackdropService,
|
||||||
|
) : ViewModel() {
|
||||||
|
fun appStart() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val prefs = preferences.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
||||||
|
if (prefs.signInAutomatically) {
|
||||||
|
val current =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
serverRepository.restoreSession(
|
||||||
|
prefs.currentServerId?.toUUIDOrNull(),
|
||||||
|
prefs.currentUserId?.toUUIDOrNull(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (current != null) {
|
||||||
|
// Restored
|
||||||
|
navigationManager.navigateTo(SetupDestination.AppContent(current))
|
||||||
|
} else {
|
||||||
|
// Did not restore
|
||||||
|
navigationManager.navigateTo(SetupDestination.ServerList)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigationManager.navigateTo(SetupDestination.Loading)
|
||||||
|
backdropService.clearBackdrop()
|
||||||
|
val currentServerId = prefs.currentServerId?.toUUIDOrNull()
|
||||||
|
if (currentServerId != null) {
|
||||||
|
val currentServer =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
serverRepository.serverDao.getServer(currentServerId)?.server
|
||||||
|
}
|
||||||
|
if (currentServer != null) {
|
||||||
|
navigationManager.navigateTo(SetupDestination.UserList(currentServer))
|
||||||
|
} else {
|
||||||
|
navigationManager.navigateTo(SetupDestination.ServerList)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigationManager.navigateTo(SetupDestination.ServerList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
// Create the mediaCodecCapabilitiesTest if needed
|
||||||
|
deviceProfileService.mediaCodecCapabilitiesTest.supportsAVC()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import com.github.damontecres.wholphin.util.EqualityMutableLiveData
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import org.jellyfin.sdk.Jellyfin
|
import org.jellyfin.sdk.Jellyfin
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.extensions.systemApi
|
import org.jellyfin.sdk.api.client.extensions.systemApi
|
||||||
|
|
@ -68,44 +69,46 @@ class ServerRepository
|
||||||
suspend fun changeUser(
|
suspend fun changeUser(
|
||||||
server: JellyfinServer,
|
server: JellyfinServer,
|
||||||
user: JellyfinUser,
|
user: JellyfinUser,
|
||||||
) = withContext(Dispatchers.IO) {
|
): CurrentUser? =
|
||||||
if (server.id != user.serverId) {
|
withContext(Dispatchers.IO) {
|
||||||
throw IllegalStateException("User is not part of the server")
|
if (server.id != user.serverId) {
|
||||||
}
|
throw IllegalStateException("User is not part of the server")
|
||||||
Timber.v("Changing user to ${user.name} on ${server.url}")
|
|
||||||
apiClient.update(baseUrl = server.url, accessToken = user.accessToken)
|
|
||||||
val userDto by apiClient.userApi.getCurrentUser()
|
|
||||||
val updatedServer =
|
|
||||||
try {
|
|
||||||
val sysInfo by apiClient.systemApi.getPublicSystemInfo()
|
|
||||||
server.copy(name = sysInfo.serverName, version = sysInfo.version)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
Timber.w(ex, "Exception fetching public system info")
|
|
||||||
server
|
|
||||||
}
|
}
|
||||||
var updatedUser =
|
Timber.v("Changing user to ${user.name} on ${server.url}")
|
||||||
user.copy(
|
apiClient.update(baseUrl = server.url, accessToken = user.accessToken)
|
||||||
id = userDto.id,
|
val userDto by apiClient.userApi.getCurrentUser()
|
||||||
name = userDto.name,
|
val updatedServer =
|
||||||
)
|
try {
|
||||||
serverDao.addOrUpdateServer(updatedServer)
|
val sysInfo by apiClient.systemApi.getPublicSystemInfo()
|
||||||
updatedUser = serverDao.addOrUpdateUser(updatedUser)
|
server.copy(name = sysInfo.serverName, version = sysInfo.version)
|
||||||
userPreferencesDataStore.updateData {
|
} catch (ex: Exception) {
|
||||||
it
|
Timber.w(ex, "Exception fetching public system info")
|
||||||
.toBuilder()
|
server
|
||||||
.apply {
|
}
|
||||||
currentServerId = updatedServer.id.toServerString()
|
var updatedUser =
|
||||||
currentUserId = updatedUser.id.toServerString()
|
user.copy(
|
||||||
}.build()
|
id = userDto.id,
|
||||||
|
name = userDto.name,
|
||||||
|
)
|
||||||
|
serverDao.addOrUpdateServer(updatedServer)
|
||||||
|
updatedUser = serverDao.addOrUpdateUser(updatedUser)
|
||||||
|
userPreferencesDataStore.updateData {
|
||||||
|
it
|
||||||
|
.toBuilder()
|
||||||
|
.apply {
|
||||||
|
currentServerId = updatedServer.id.toServerString()
|
||||||
|
currentUserId = updatedUser.id.toServerString()
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_current.value = CurrentUser(updatedServer, updatedUser, userDto)
|
||||||
|
}
|
||||||
|
sharedPreferences.edit(true) {
|
||||||
|
putString(SERVER_URL_KEY, updatedServer.url)
|
||||||
|
putString(ACCESS_TOKEN_KEY, updatedUser.accessToken)
|
||||||
|
}
|
||||||
|
return@withContext _current.value
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
_current.value = CurrentUser(updatedServer, updatedUser, userDto)
|
|
||||||
}
|
|
||||||
sharedPreferences.edit(true) {
|
|
||||||
putString(SERVER_URL_KEY, updatedServer.url)
|
|
||||||
putString(ACCESS_TOKEN_KEY, updatedUser.accessToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores a session for the given server & user such as when the app reopens
|
* Restores a session for the given server & user such as when the app reopens
|
||||||
|
|
@ -115,10 +118,10 @@ class ServerRepository
|
||||||
suspend fun restoreSession(
|
suspend fun restoreSession(
|
||||||
serverId: UUID?,
|
serverId: UUID?,
|
||||||
userId: UUID?,
|
userId: UUID?,
|
||||||
): Boolean {
|
): CurrentUser? {
|
||||||
if (serverId == null || userId == null) {
|
if (serverId == null || userId == null) {
|
||||||
_current.setValueOnMain(null)
|
_current.setValueOnMain(null)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
val serverAndUsers =
|
val serverAndUsers =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
@ -129,13 +132,17 @@ class ServerRepository
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
// TODO pin-related
|
// TODO pin-related
|
||||||
// if (user != null && !user.hasPin) {
|
// if (user != null && !user.hasPin) {
|
||||||
changeUser(serverAndUsers.server, user)
|
return changeUser(serverAndUsers.server, user)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun fetchLastUsedServer(serverId: UUID?): JellyfinServer? =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
serverId?.let { serverDao.getServer(serverId)?.server }
|
||||||
|
}
|
||||||
|
|
||||||
fun closeSession() {
|
fun closeSession() {
|
||||||
_current.value = null
|
_current.value = null
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +177,7 @@ class ServerRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
changeUser(server, user)
|
return@withContext changeUser(server, user)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalArgumentException("Authentication result's user was null")
|
throw IllegalArgumentException("Authentication result's user was null")
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +267,7 @@ class ServerRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class CurrentUser(
|
data class CurrentUser(
|
||||||
val server: JellyfinServer,
|
val server: JellyfinServer,
|
||||||
val user: JellyfinUser,
|
val user: JellyfinUser,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ data class JellyfinServer(
|
||||||
],
|
],
|
||||||
indices = [Index("id", "serverId", unique = true)],
|
indices = [Index("id", "serverId", unique = true)],
|
||||||
)
|
)
|
||||||
|
@Serializable
|
||||||
data class JellyfinUser(
|
data class JellyfinUser(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val rowId: Int = 0,
|
val rowId: Int = 0,
|
||||||
|
|
|
||||||
|
|
@ -851,8 +851,7 @@ val basicPreferences =
|
||||||
title = R.string.ui_interface,
|
title = R.string.ui_interface,
|
||||||
preferences =
|
preferences =
|
||||||
listOf(
|
listOf(
|
||||||
// TODO PIN-related
|
AppPreference.SignInAuto,
|
||||||
// AppPreference.SignInAuto,
|
|
||||||
AppPreference.HomePageItems,
|
AppPreference.HomePageItems,
|
||||||
AppPreference.CombineContinueNext,
|
AppPreference.CombineContinueNext,
|
||||||
AppPreference.RewatchNextUp,
|
AppPreference.RewatchNextUp,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class DeviceProfileService
|
||||||
constructor(
|
constructor(
|
||||||
@param:ApplicationContext private val context: Context,
|
@param:ApplicationContext private val context: Context,
|
||||||
) {
|
) {
|
||||||
private val mediaCodecCapabilitiesTest by lazy {
|
val mediaCodecCapabilitiesTest by lazy {
|
||||||
// Created lazily below on the IO thread since it cn take time
|
// Created lazily below on the IO thread since it cn take time
|
||||||
MediaCodecCapabilitiesTest(context)
|
MediaCodecCapabilitiesTest(context)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,7 @@ class NavigationManager
|
||||||
*/
|
*/
|
||||||
fun navigateToFromDrawer(destination: Destination) {
|
fun navigateToFromDrawer(destination: Destination) {
|
||||||
goToHome()
|
goToHome()
|
||||||
if (destination == Destination.ServerList || destination is Destination.UserList) {
|
backStack.add(destination)
|
||||||
backStack.add(destination)
|
|
||||||
(0..<backStack.size - 1).forEach { _ -> backStack.removeAt(0) }
|
|
||||||
} else {
|
|
||||||
backStack.add(destination)
|
|
||||||
}
|
|
||||||
log()
|
log()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
import com.github.damontecres.wholphin.data.CurrentUser
|
||||||
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.acra.ACRA
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages navigating for setup
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class SetupNavigationManager
|
||||||
|
@Inject
|
||||||
|
constructor() {
|
||||||
|
var backStack: MutableList<NavKey> = mutableStateListOf(SetupDestination.Loading)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the specified [SetupDestination]
|
||||||
|
*/
|
||||||
|
fun navigateTo(destination: SetupDestination) {
|
||||||
|
backStack[0] = destination
|
||||||
|
log()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log() {
|
||||||
|
val dest = backStack.lastOrNull().toString()
|
||||||
|
Timber.i("Current setup destination: %s", dest)
|
||||||
|
ACRA.errorReporter.putCustomData("setupDestination", dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface SetupDestination : NavKey {
|
||||||
|
@Serializable
|
||||||
|
data object Loading : SetupDestination
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object ServerList : SetupDestination
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UserList(
|
||||||
|
val server: JellyfinServer,
|
||||||
|
) : SetupDestination
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AppContent(
|
||||||
|
val current: CurrentUser,
|
||||||
|
) : SetupDestination
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.saveable.rememberSerializable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
|
@ -24,8 +25,12 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||||
|
import androidx.navigation3.runtime.NavBackStack
|
||||||
import androidx.navigation3.runtime.NavEntry
|
import androidx.navigation3.runtime.NavEntry
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||||
|
import androidx.navigation3.runtime.serialization.NavBackStackSerializer
|
||||||
|
import androidx.navigation3.runtime.serialization.NavKeySerializer
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
|
|
@ -62,13 +67,22 @@ class ApplicationContentViewModel
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ApplicationContent(
|
fun ApplicationContent(
|
||||||
server: JellyfinServer?,
|
server: JellyfinServer,
|
||||||
user: JellyfinUser?,
|
user: JellyfinUser,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
preferences: UserPreferences,
|
preferences: UserPreferences,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ApplicationContentViewModel = hiltViewModel(),
|
viewModel: ApplicationContentViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val backStack: MutableList<NavKey> =
|
||||||
|
rememberSerializable(
|
||||||
|
server,
|
||||||
|
user,
|
||||||
|
serializer = NavBackStackSerializer(elementSerializer = NavKeySerializer()),
|
||||||
|
) {
|
||||||
|
NavBackStack(Destination.Home())
|
||||||
|
}
|
||||||
|
navigationManager.backStack = backStack
|
||||||
val backdrop by viewModel.backdropService.backdropFlow.collectAsStateWithLifecycle()
|
val backdrop by viewModel.backdropService.backdropFlow.collectAsStateWithLifecycle()
|
||||||
val backdropStyle = preferences.appPreferences.interfacePreferences.backdropStyle
|
val backdropStyle = preferences.appPreferences.interfacePreferences.backdropStyle
|
||||||
Box(
|
Box(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import androidx.navigation3.runtime.NavKey
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
|
||||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode
|
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
||||||
|
|
@ -28,14 +27,6 @@ import java.util.UUID
|
||||||
sealed class Destination(
|
sealed class Destination(
|
||||||
val fullScreen: Boolean = false,
|
val fullScreen: Boolean = false,
|
||||||
) : NavKey {
|
) : NavKey {
|
||||||
@Serializable
|
|
||||||
data object ServerList : Destination(true)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class UserList(
|
|
||||||
val server: JellyfinServer,
|
|
||||||
) : Destination(true)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Home(
|
data class Home(
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ import com.github.damontecres.wholphin.ui.main.SearchPage
|
||||||
import com.github.damontecres.wholphin.ui.playback.PlaybackPage
|
import com.github.damontecres.wholphin.ui.playback.PlaybackPage
|
||||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesPage
|
import com.github.damontecres.wholphin.ui.preferences.PreferencesPage
|
||||||
import com.github.damontecres.wholphin.ui.setup.InstallUpdatePage
|
import com.github.damontecres.wholphin.ui.setup.InstallUpdatePage
|
||||||
import com.github.damontecres.wholphin.ui.setup.SwitchServerContent
|
|
||||||
import com.github.damontecres.wholphin.ui.setup.SwitchUserContent
|
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.CollectionType
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -66,14 +64,6 @@ fun DestinationContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Destination.ServerList -> {
|
|
||||||
SwitchServerContent(modifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Destination.UserList -> {
|
|
||||||
SwitchUserContent(destination.server, modifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Destination.Settings -> {
|
is Destination.Settings -> {
|
||||||
PreferencesPage(
|
PreferencesPage(
|
||||||
preferences.appPreferences,
|
preferences.appPreferences,
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ import com.github.damontecres.wholphin.preferences.AppThemeColors
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.SetupDestination
|
||||||
|
import com.github.damontecres.wholphin.services.SetupNavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.components.TimeDisplay
|
import com.github.damontecres.wholphin.ui.components.TimeDisplay
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
|
|
@ -106,6 +108,7 @@ class NavDrawerViewModel
|
||||||
constructor(
|
constructor(
|
||||||
private val navDrawerItemRepository: NavDrawerItemRepository,
|
private val navDrawerItemRepository: NavDrawerItemRepository,
|
||||||
val navigationManager: NavigationManager,
|
val navigationManager: NavigationManager,
|
||||||
|
val setupNavigationManager: SetupNavigationManager,
|
||||||
val backdropService: BackdropService,
|
val backdropService: BackdropService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private var all: List<NavDrawerItem>? = null
|
private var all: List<NavDrawerItem>? = null
|
||||||
|
|
@ -361,10 +364,8 @@ fun NavDrawer(
|
||||||
drawerOpen = drawerState.isOpen,
|
drawerOpen = drawerState.isOpen,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.navigationManager.navigateToFromDrawer(
|
viewModel.setupNavigationManager.navigateTo(
|
||||||
Destination.UserList(
|
SetupDestination.UserList(server),
|
||||||
server,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(start = drawerPadding),
|
modifier = Modifier.padding(start = drawerPadding),
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import com.github.damontecres.wholphin.R
|
||||||
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
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.SetupDestination
|
||||||
|
import com.github.damontecres.wholphin.services.SetupNavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
import com.github.damontecres.wholphin.ui.showToast
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
|
@ -41,7 +41,7 @@ class SwitchServerViewModel
|
||||||
val jellyfin: Jellyfin,
|
val jellyfin: Jellyfin,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
val serverDao: JellyfinServerDao,
|
val serverDao: JellyfinServerDao,
|
||||||
val navigationManager: NavigationManager,
|
val navigationManager: SetupNavigationManager,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val servers = MutableLiveData<List<JellyfinServer>>(listOf())
|
val servers = MutableLiveData<List<JellyfinServer>>(listOf())
|
||||||
val serverStatus = MutableLiveData<Map<UUID, ServerConnectionStatus>>(mapOf())
|
val serverStatus = MutableLiveData<Map<UUID, ServerConnectionStatus>>(mapOf())
|
||||||
|
|
@ -156,7 +156,7 @@ class SwitchServerViewModel
|
||||||
)
|
)
|
||||||
serverRepository.addAndChangeServer(updatedServer)
|
serverRepository.addAndChangeServer(updatedServer)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
navigationManager.navigateTo(Destination.UserList(server))
|
navigationManager.navigateTo(SetupDestination.UserList(updatedServer))
|
||||||
}
|
}
|
||||||
} else if (result is ServerConnectionStatus.Error) {
|
} else if (result is ServerConnectionStatus.Error) {
|
||||||
showToast(context, "Error connecting: $${result.message}")
|
showToast(context, "Error connecting: $${result.message}")
|
||||||
|
|
@ -201,7 +201,7 @@ class SwitchServerViewModel
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
addServerState.value = LoadingState.Success
|
addServerState.value = LoadingState.Success
|
||||||
navigationManager.navigateTo(Destination.UserList(server))
|
navigationManager.navigateTo(SetupDestination.UserList(server))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||||
|
import com.github.damontecres.wholphin.services.SetupDestination
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
|
|
@ -130,7 +131,9 @@ fun SwitchUserContent(
|
||||||
viewModel.removeUser(user)
|
viewModel.removeUser(user)
|
||||||
},
|
},
|
||||||
onSwitchServer = {
|
onSwitchServer = {
|
||||||
viewModel.navigationManager.navigateTo(Destination.ServerList)
|
viewModel.setupNavigationManager.navigateTo(
|
||||||
|
SetupDestination.ServerList,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||||
import com.github.damontecres.wholphin.services.ImageUrlService
|
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.SetupDestination
|
||||||
|
import com.github.damontecres.wholphin.services.SetupNavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
|
@ -43,6 +45,7 @@ class SwitchUserViewModel
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
val serverDao: JellyfinServerDao,
|
val serverDao: JellyfinServerDao,
|
||||||
val navigationManager: NavigationManager,
|
val navigationManager: NavigationManager,
|
||||||
|
val setupNavigationManager: SetupNavigationManager,
|
||||||
val imageUrlService: ImageUrlService,
|
val imageUrlService: ImageUrlService,
|
||||||
@Assisted val server: JellyfinServer,
|
@Assisted val server: JellyfinServer,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
@ -120,9 +123,11 @@ class SwitchUserViewModel
|
||||||
fun switchUser(user: JellyfinUser) {
|
fun switchUser(user: JellyfinUser) {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
serverRepository.changeUser(server, user)
|
val current = serverRepository.changeUser(server, user)
|
||||||
withContext(Dispatchers.Main) {
|
if (current != null) {
|
||||||
navigationManager.goToHome()
|
withContext(Dispatchers.Main) {
|
||||||
|
setupNavigationManager.navigateTo(SetupDestination.AppContent(current))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error switching user")
|
Timber.e(ex, "Error switching user")
|
||||||
|
|
@ -144,9 +149,11 @@ class SwitchUserViewModel
|
||||||
username = username,
|
username = username,
|
||||||
password = password,
|
password = password,
|
||||||
)
|
)
|
||||||
serverRepository.changeUser(server.url, authenticationResult)
|
val current = serverRepository.changeUser(server.url, authenticationResult)
|
||||||
withContext(Dispatchers.Main) {
|
if (current != null) {
|
||||||
navigationManager.goToHome()
|
withContext(Dispatchers.Main) {
|
||||||
|
setupNavigationManager.navigateTo(SetupDestination.AppContent(current))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error logging in user")
|
Timber.e(ex, "Error logging in user")
|
||||||
|
|
@ -192,9 +199,13 @@ class SwitchUserViewModel
|
||||||
val authenticationResult by api.userApi.authenticateWithQuickConnect(
|
val authenticationResult by api.userApi.authenticateWithQuickConnect(
|
||||||
QuickConnectDto(secret = state.secret),
|
QuickConnectDto(secret = state.secret),
|
||||||
)
|
)
|
||||||
serverRepository.changeUser(server.url, authenticationResult)
|
val current = serverRepository.changeUser(server.url, authenticationResult)
|
||||||
withContext(Dispatchers.Main) {
|
if (current != null) {
|
||||||
navigationManager.goToHome()
|
withContext(Dispatchers.Main) {
|
||||||
|
setupNavigationManager.navigateTo(
|
||||||
|
SetupDestination.AppContent(current),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error during quick connect")
|
Timber.e(ex, "Error during quick connect")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue