mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Fixes a few UI issues (#27)
Fixes: - Rounded corners on dialogs during playback - Follow up to #23 to show errors on Main thread - Show login errors higher up to ensure they aren't blocked by the keyboard - Faster tracks switching by not recomposing the player surface - Refocus on controller buttons after switching tracks - Show playback errors when they occur
This commit is contained in:
parent
6194eba8c5
commit
e0772ed9b8
5 changed files with 118 additions and 64 deletions
|
|
@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
|
@ -306,6 +307,7 @@ fun LeftPlaybackButtons(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var showMoreOptions by remember { mutableStateOf(false) }
|
var showMoreOptions by remember { mutableStateOf(false) }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.focusGroup(),
|
modifier = modifier.focusGroup(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(buttonSpacing),
|
horizontalArrangement = Arrangement.spacedBy(buttonSpacing),
|
||||||
|
|
@ -319,6 +321,7 @@ fun LeftPlaybackButtons(
|
||||||
},
|
},
|
||||||
enabled = true,
|
enabled = true,
|
||||||
onControllerInteraction = onControllerInteraction,
|
onControllerInteraction = onControllerInteraction,
|
||||||
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showMoreOptions) {
|
if (showMoreOptions) {
|
||||||
|
|
@ -329,7 +332,10 @@ fun LeftPlaybackButtons(
|
||||||
}
|
}
|
||||||
BottomDialog(
|
BottomDialog(
|
||||||
choices = options,
|
choices = options,
|
||||||
onDismissRequest = { showMoreOptions = false },
|
onDismissRequest = {
|
||||||
|
showMoreOptions = false
|
||||||
|
focusRequester.tryRequestFocus()
|
||||||
|
},
|
||||||
onSelectChoice = { index, choice ->
|
onSelectChoice = { index, choice ->
|
||||||
val action = moreButtonOptions.options[choice] ?: PlaybackAction.ShowDebug
|
val action = moreButtonOptions.options[choice] ?: PlaybackAction.ShowDebug
|
||||||
onPlaybackActionClick.invoke(action)
|
onPlaybackActionClick.invoke(action)
|
||||||
|
|
@ -359,6 +365,10 @@ fun RightPlaybackButtons(
|
||||||
var showAudioDialog by remember { mutableStateOf(false) }
|
var showAudioDialog by remember { mutableStateOf(false) }
|
||||||
var showSpeedDialog by remember { mutableStateOf(false) }
|
var showSpeedDialog by remember { mutableStateOf(false) }
|
||||||
var showScaleDialog by remember { mutableStateOf(false) }
|
var showScaleDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val captionFocusRequester = remember { FocusRequester() }
|
||||||
|
val settingsFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.focusGroup(),
|
modifier = modifier.focusGroup(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(buttonSpacing),
|
horizontalArrangement = Arrangement.spacedBy(buttonSpacing),
|
||||||
|
|
@ -372,6 +382,7 @@ fun RightPlaybackButtons(
|
||||||
showCaptionDialog = true
|
showCaptionDialog = true
|
||||||
},
|
},
|
||||||
onControllerInteraction = onControllerInteraction,
|
onControllerInteraction = onControllerInteraction,
|
||||||
|
modifier = Modifier.focusRequester(captionFocusRequester),
|
||||||
)
|
)
|
||||||
// Playback speed, etc
|
// Playback speed, etc
|
||||||
PlaybackButton(
|
PlaybackButton(
|
||||||
|
|
@ -382,8 +393,10 @@ fun RightPlaybackButtons(
|
||||||
},
|
},
|
||||||
enabled = true,
|
enabled = true,
|
||||||
onControllerInteraction = onControllerInteraction,
|
onControllerInteraction = onControllerInteraction,
|
||||||
|
modifier = Modifier.focusRequester(settingsFocusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
if (showCaptionDialog) {
|
if (showCaptionDialog) {
|
||||||
val options = subtitleStreams.map { it.displayName }
|
val options = subtitleStreams.map { it.displayName }
|
||||||
Timber.v("subtitleIndex=$subtitleIndex, options=$options")
|
Timber.v("subtitleIndex=$subtitleIndex, options=$options")
|
||||||
|
|
@ -395,6 +408,11 @@ fun RightPlaybackButtons(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onControllerInteraction.invoke()
|
onControllerInteraction.invoke()
|
||||||
showCaptionDialog = false
|
showCaptionDialog = false
|
||||||
|
scope.launch {
|
||||||
|
// TODO this is hacky, but playback changes force refocus and this is a workaround
|
||||||
|
delay(250L)
|
||||||
|
captionFocusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSelectChoice = { index, _ ->
|
onSelectChoice = { index, _ ->
|
||||||
val send =
|
val send =
|
||||||
|
|
@ -434,6 +452,10 @@ fun RightPlaybackButtons(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onControllerInteraction.invoke()
|
onControllerInteraction.invoke()
|
||||||
showAudioDialog = false
|
showAudioDialog = false
|
||||||
|
scope.launch {
|
||||||
|
delay(250L)
|
||||||
|
settingsFocusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSelectChoice = { index, _ ->
|
onSelectChoice = { index, _ ->
|
||||||
onPlaybackActionClick.invoke(PlaybackAction.ToggleAudio(audioStreams[index].index))
|
onPlaybackActionClick.invoke(PlaybackAction.ToggleAudio(audioStreams[index].index))
|
||||||
|
|
@ -448,6 +470,10 @@ fun RightPlaybackButtons(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onControllerInteraction.invoke()
|
onControllerInteraction.invoke()
|
||||||
showSpeedDialog = false
|
showSpeedDialog = false
|
||||||
|
scope.launch {
|
||||||
|
delay(250L)
|
||||||
|
settingsFocusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSelectChoice = { _, value ->
|
onSelectChoice = { _, value ->
|
||||||
onPlaybackActionClick.invoke(PlaybackAction.PlaybackSpeed(value.toFloat()))
|
onPlaybackActionClick.invoke(PlaybackAction.PlaybackSpeed(value.toFloat()))
|
||||||
|
|
@ -462,6 +488,10 @@ fun RightPlaybackButtons(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onControllerInteraction.invoke()
|
onControllerInteraction.invoke()
|
||||||
showScaleDialog = false
|
showScaleDialog = false
|
||||||
|
scope.launch {
|
||||||
|
delay(250L)
|
||||||
|
settingsFocusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSelectChoice = { index, _ ->
|
onSelectChoice = { index, _ ->
|
||||||
onPlaybackActionClick.invoke(PlaybackAction.Scale(playbackScaleOptions.keys.toList()[index]))
|
onPlaybackActionClick.invoke(PlaybackAction.Scale(playbackScaleOptions.keys.toList()[index]))
|
||||||
|
|
@ -601,7 +631,7 @@ private fun BottomDialog(
|
||||||
Modifier
|
Modifier
|
||||||
.wrapContentSize()
|
.wrapContentSize()
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.background(Color.DarkGray),
|
.background(Color.DarkGray, shape = RoundedCornerShape(16.dp)),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier =
|
modifier =
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.input.key.onKeyEvent
|
import androidx.compose.ui.input.key.onKeyEvent
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
|
@ -61,9 +60,11 @@ import com.github.damontecres.wholphin.data.model.Playlist
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.skipBackOnResume
|
import com.github.damontecres.wholphin.preferences.skipBackOnResume
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
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.seasonEpisode
|
import com.github.damontecres.wholphin.util.seasonEpisode
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.jellyfin.sdk.model.api.DeviceProfile
|
import org.jellyfin.sdk.model.api.DeviceProfile
|
||||||
|
|
@ -84,60 +85,62 @@ fun PlaybackPage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: PlaybackViewModel = hiltViewModel(),
|
viewModel: PlaybackViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val prefs = preferences.appPreferences.playbackPreferences
|
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
LaunchedEffect(destination.itemId) {
|
LaunchedEffect(destination.itemId) {
|
||||||
viewModel.init(destination, deviceProfile, preferences)
|
viewModel.init(destination, deviceProfile, preferences)
|
||||||
}
|
}
|
||||||
val player = viewModel.player
|
|
||||||
val stream by viewModel.stream.observeAsState(null)
|
|
||||||
|
|
||||||
val title by viewModel.title.observeAsState(null)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
val subtitle by viewModel.subtitle.observeAsState(null)
|
when (val st = loading) {
|
||||||
val duration by viewModel.duration.observeAsState(null)
|
is LoadingState.Error -> ErrorMessage(st, modifier)
|
||||||
val audioStreams by viewModel.audioStreams.observeAsState(listOf())
|
LoadingState.Pending,
|
||||||
val subtitleStreams by viewModel.subtitleStreams.observeAsState(listOf())
|
LoadingState.Loading,
|
||||||
val trickplay by viewModel.trickplay.observeAsState(null)
|
-> LoadingPage(modifier)
|
||||||
val chapters by viewModel.chapters.observeAsState(listOf())
|
|
||||||
val currentPlayback by viewModel.currentPlayback.observeAsState(null)
|
|
||||||
val currentItemPlayback by viewModel.currentItemPlayback.observeAsState(
|
|
||||||
ItemPlayback(
|
|
||||||
userId = -1,
|
|
||||||
itemId = UUID.randomUUID(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val currentSegment by viewModel.currentSegment.observeAsState(null)
|
|
||||||
var segmentCancelled by remember(currentSegment?.id) { mutableStateOf(false) }
|
|
||||||
|
|
||||||
var cues by remember { mutableStateOf<List<Cue>>(listOf()) }
|
LoadingState.Success -> {
|
||||||
var showDebugInfo by remember { mutableStateOf(prefs.showDebugInfo) }
|
val prefs = preferences.appPreferences.playbackPreferences
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val nextUp by viewModel.nextUp.observeAsState(null)
|
val player = viewModel.player
|
||||||
val playlist by viewModel.playlist.observeAsState(Playlist(listOf()))
|
val title by viewModel.title.observeAsState(null)
|
||||||
|
val subtitle by viewModel.subtitle.observeAsState(null)
|
||||||
|
val duration by viewModel.duration.observeAsState(null)
|
||||||
|
val audioStreams by viewModel.audioStreams.observeAsState(listOf())
|
||||||
|
val subtitleStreams by viewModel.subtitleStreams.observeAsState(listOf())
|
||||||
|
val trickplay by viewModel.trickplay.observeAsState(null)
|
||||||
|
val chapters by viewModel.chapters.observeAsState(listOf())
|
||||||
|
val currentPlayback by viewModel.currentPlayback.observeAsState(null)
|
||||||
|
val currentItemPlayback by viewModel.currentItemPlayback.observeAsState(
|
||||||
|
ItemPlayback(
|
||||||
|
userId = -1,
|
||||||
|
itemId = UUID.randomUUID(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val currentSegment by viewModel.currentSegment.observeAsState(null)
|
||||||
|
var segmentCancelled by remember(currentSegment?.id) { mutableStateOf(false) }
|
||||||
|
|
||||||
// TODO move to viewmodel?
|
var cues by remember { mutableStateOf<List<Cue>>(listOf()) }
|
||||||
val cueListener =
|
var showDebugInfo by remember { mutableStateOf(prefs.showDebugInfo) }
|
||||||
remember {
|
|
||||||
object : Player.Listener {
|
val nextUp by viewModel.nextUp.observeAsState(null)
|
||||||
override fun onCues(cueGroup: CueGroup) {
|
val playlist by viewModel.playlist.observeAsState(Playlist(listOf()))
|
||||||
cues = cueGroup.cues
|
|
||||||
|
// TODO move to viewmodel?
|
||||||
|
val cueListener =
|
||||||
|
remember {
|
||||||
|
object : Player.Listener {
|
||||||
|
override fun onCues(cueGroup: CueGroup) {
|
||||||
|
cues = cueGroup.cues
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OneTimeLaunchedEffect {
|
||||||
|
player.addListener(cueListener)
|
||||||
}
|
}
|
||||||
}
|
DisposableEffect(Unit) {
|
||||||
|
onDispose { player.removeListener(cueListener) }
|
||||||
OneTimeLaunchedEffect {
|
}
|
||||||
player.addListener(cueListener)
|
AmbientPlayerListener(player)
|
||||||
}
|
|
||||||
DisposableEffect(Unit) {
|
|
||||||
onDispose { player.removeListener(cueListener) }
|
|
||||||
}
|
|
||||||
AmbientPlayerListener(player)
|
|
||||||
|
|
||||||
if (stream == null) {
|
|
||||||
LoadingPage()
|
|
||||||
} else {
|
|
||||||
stream?.let {
|
|
||||||
var contentScale by remember { mutableStateOf(ContentScale.Fit) }
|
var contentScale by remember { mutableStateOf(ContentScale.Fit) }
|
||||||
var playbackSpeed by remember { mutableFloatStateOf(1.0f) }
|
var playbackSpeed by remember { mutableFloatStateOf(1.0f) }
|
||||||
LaunchedEffect(playbackSpeed) { player.setPlaybackSpeed(playbackSpeed) }
|
LaunchedEffect(playbackSpeed) { player.setPlaybackSpeed(playbackSpeed) }
|
||||||
|
|
@ -146,6 +149,7 @@ fun PlaybackPage(
|
||||||
val scaledModifier =
|
val scaledModifier =
|
||||||
Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp)
|
Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp)
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val controllerFocusRequester = remember { FocusRequester() }
|
||||||
val playPauseState = rememberPlayPauseButtonState(player)
|
val playPauseState = rememberPlayPauseButtonState(player)
|
||||||
val seekBarState = rememberSeekBarState(player, scope)
|
val seekBarState = rememberSeekBarState(player, scope)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.Format
|
import androidx.media3.common.Format
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.TrackSelectionOverride
|
import androidx.media3.common.TrackSelectionOverride
|
||||||
import androidx.media3.common.Tracks
|
import androidx.media3.common.Tracks
|
||||||
|
|
@ -32,6 +33,8 @@ import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.ui.toServerString
|
import com.github.damontecres.wholphin.ui.toServerString
|
||||||
import com.github.damontecres.wholphin.util.EqualityMutableLiveData
|
import com.github.damontecres.wholphin.util.EqualityMutableLiveData
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
|
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
|
||||||
import com.github.damontecres.wholphin.util.TrackSupport
|
import com.github.damontecres.wholphin.util.TrackSupport
|
||||||
import com.github.damontecres.wholphin.util.checkForSupport
|
import com.github.damontecres.wholphin.util.checkForSupport
|
||||||
|
|
@ -105,7 +108,7 @@ class PlaybackViewModel
|
||||||
playWhenReady = true
|
playWhenReady = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val stream = MutableLiveData<StreamDecision?>(null)
|
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||||
|
|
||||||
val title = MutableLiveData<String?>(null)
|
val title = MutableLiveData<String?>(null)
|
||||||
val subtitle = MutableLiveData<String?>(null)
|
val subtitle = MutableLiveData<String?>(null)
|
||||||
|
|
@ -152,7 +155,12 @@ class PlaybackViewModel
|
||||||
val itemId = destination.itemId
|
val itemId = destination.itemId
|
||||||
this.itemId = itemId
|
this.itemId = itemId
|
||||||
val item = destination.item
|
val item = destination.item
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launch(
|
||||||
|
LoadingExceptionHandler(
|
||||||
|
loading,
|
||||||
|
"Error preparing for plaback on ${destination.itemId}",
|
||||||
|
) + Dispatchers.IO,
|
||||||
|
) {
|
||||||
val queriedItem = item?.data ?: api.userLibraryApi.getItem(itemId).content
|
val queriedItem = item?.data ?: api.userLibraryApi.getItem(itemId).content
|
||||||
val base =
|
val base =
|
||||||
if (queriedItem.type == BaseItemKind.PLAYLIST) {
|
if (queriedItem.type == BaseItemKind.PLAYLIST) {
|
||||||
|
|
@ -493,7 +501,7 @@ class PlaybackViewModel
|
||||||
this@PlaybackViewModel.activityListener = activityListener
|
this@PlaybackViewModel.activityListener = activityListener
|
||||||
|
|
||||||
duration.value = source.runTimeTicks?.ticks
|
duration.value = source.runTimeTicks?.ticks
|
||||||
stream.value = decision
|
loading.value = LoadingState.Success
|
||||||
currentPlayback.value = playback
|
currentPlayback.value = playback
|
||||||
this@PlaybackViewModel.currentItemPlayback.value = itemPlayback
|
this@PlaybackViewModel.currentItemPlayback.value = itemPlayback
|
||||||
player.setMediaItem(
|
player.setMediaItem(
|
||||||
|
|
@ -708,6 +716,13 @@ class PlaybackViewModel
|
||||||
tracks = checkForSupport(tracks),
|
tracks = checkForSupport(tracks),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
|
Timber.e(error, "Playback error")
|
||||||
|
viewModelScope.launch(Dispatchers.Main + ExceptionHandler()) {
|
||||||
|
loading.value = LoadingState.Error("Error during playback", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CurrentPlayback(
|
data class CurrentPlayback(
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,16 @@ fun SwitchUserContent(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
|
when (val s = userState) {
|
||||||
|
is LoadingState.Error -> {
|
||||||
|
Text(
|
||||||
|
text = s.message ?: s.exception?.localizedMessage ?: "Error",
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.focusGroup(),
|
modifier = Modifier.focusGroup(),
|
||||||
|
|
@ -236,16 +246,6 @@ fun SwitchUserContent(
|
||||||
Text("Login")
|
Text("Login")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (val s = userState) {
|
|
||||||
is LoadingState.Error -> {
|
|
||||||
Text(
|
|
||||||
text = s.message ?: s.exception?.localizedMessage ?: "Error",
|
|
||||||
color = MaterialTheme.colorScheme.error,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error switching user")
|
Timber.e(ex, "Error switching user")
|
||||||
switchUserState.value = LoadingState.Error(exception = ex)
|
setError(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +178,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error logging in user")
|
Timber.e(ex, "Error logging in user")
|
||||||
switchUserState.value = LoadingState.Error(ex)
|
setError(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +230,7 @@ class SwitchUserViewModel
|
||||||
put(server.id, false)
|
put(server.id, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switchUserState.value = LoadingState.Error(ex)
|
setError(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,4 +328,9 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun setError(ex: Exception) =
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
switchUserState.value = LoadingState.Error(ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue