Fixes to v1.0.7 (#1953)
## Description Collection of minor bug fixes for `v1.0.7` - Fixes a possible race condition crash when navigating to the search page - Uses friendlier error message when login fails due to parental restrictions (based on [`jellyfin-web`](https://github.com/jellyfin/jellyfin-web/blob/v10.11.11/src/controllers/session/login/index.js#L38-L41)) - Don't re-sort search results by name, just by relevance scoring - Consume the key event when dim is active (ie if the screen is dimmed, pressing a key will un-dim and not perform another action) ### Related issues Fixes #1922 Fixes #1951 Related to #1848 (search sorting) ### Testing Emulator & nvidia shield ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
6486bd19ed
commit
616565f671
6 changed files with 70 additions and 28 deletions
|
|
@ -288,7 +288,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
if (screensaverService.state.value.show) {
|
if (screensaverService.state.value.run { show || showDim }) {
|
||||||
screensaverService.stop(false)
|
screensaverService.stop(false)
|
||||||
screensaverService.pulse()
|
screensaverService.pulse()
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -418,12 +418,16 @@ fun SearchPage(
|
||||||
itemsIndexed(state.includedSearchableTypes) { index, type ->
|
itemsIndexed(state.includedSearchableTypes) { index, type ->
|
||||||
val rowIndex = RESULTS_START + index
|
val rowIndex = RESULTS_START + index
|
||||||
val result = state.results.getOrDefault(type, SearchResult.Searching)
|
val result = state.results.getOrDefault(type, SearchResult.Searching)
|
||||||
|
val focusRequester =
|
||||||
|
remember(state.includedSearchableTypes.size) {
|
||||||
|
focusRequesters.getOrNull(rowIndex) ?: FocusRequester()
|
||||||
|
}
|
||||||
SearchRowResult(
|
SearchRowResult(
|
||||||
title = type.titleStringRes,
|
title = type.titleStringRes,
|
||||||
result = result,
|
result = result,
|
||||||
rowIndex = rowIndex,
|
rowIndex = rowIndex,
|
||||||
position = position,
|
position = position,
|
||||||
focusRequester = focusRequesters[rowIndex],
|
focusRequester = focusRequester,
|
||||||
onClickItem = onClickItem,
|
onClickItem = onClickItem,
|
||||||
onLongClickItem = { index, item ->
|
onLongClickItem = { index, item ->
|
||||||
onLongClickItem(rowIndex, index, item)
|
onLongClickItem(rowIndex, index, item)
|
||||||
|
|
|
||||||
|
|
@ -273,8 +273,7 @@ class SearchViewModel
|
||||||
}
|
}
|
||||||
val sorted =
|
val sorted =
|
||||||
items.sortedWith(
|
items.sortedWith(
|
||||||
compareBy<BaseItem> { SearchRelevance.score(it, query) }
|
compareBy<BaseItem> { SearchRelevance.score(it, query) },
|
||||||
.thenBy { it.sortName },
|
|
||||||
)
|
)
|
||||||
Timber.v("Search finished for %s, %s results", type, sorted.size)
|
Timber.v("Search finished for %s, %s results", type, sorted.size)
|
||||||
_state.value.results[type] = SearchResult.Success(sorted)
|
_state.value.results[type] = SearchResult.Success(sorted)
|
||||||
|
|
@ -307,8 +306,7 @@ class SearchViewModel
|
||||||
}
|
}
|
||||||
val sorted =
|
val sorted =
|
||||||
items.sortedWith(
|
items.sortedWith(
|
||||||
compareBy<BaseItem> { SearchRelevance.score(it, query) }
|
compareBy<BaseItem> { SearchRelevance.score(it, query) },
|
||||||
.thenBy { it.name ?: "" },
|
|
||||||
)
|
)
|
||||||
Timber.v("searchCombined complete %s results", sorted.size)
|
Timber.v("searchCombined complete %s results", sorted.size)
|
||||||
_state.update { it.copy(combinedResults = SearchResult.Success(sorted)) }
|
_state.update { it.copy(combinedResults = SearchResult.Success(sorted)) }
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,24 @@ fun SwitchUserContent(
|
||||||
showAddUser = false
|
showAddUser = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun trySwitchUser(user: JellyfinUser) {
|
||||||
|
val result = viewModel.trySwitchUser(user)
|
||||||
|
scope.launch {
|
||||||
|
when (val r = result.await()) {
|
||||||
|
is SwitchUserResult.Error -> {
|
||||||
|
Toast.makeText(context, r.errorMessage, Toast.LENGTH_LONG).show()
|
||||||
|
if (r.showLogin) {
|
||||||
|
showAddUserDialog(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchUserResult.Success -> {
|
||||||
|
// no-op, view model will navigate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(state.switchUserState) {
|
LaunchedEffect(state.switchUserState) {
|
||||||
if (!showAddUser) {
|
if (!showAddUser) {
|
||||||
when (val s = state.switchUserState) {
|
when (val s = state.switchUserState) {
|
||||||
|
|
@ -148,13 +166,7 @@ fun SwitchUserContent(
|
||||||
} else if (user.hasPin) {
|
} else if (user.hasPin) {
|
||||||
switchUserWithPin = user
|
switchUserWithPin = user
|
||||||
} else {
|
} else {
|
||||||
val result = viewModel.trySwitchUser(user)
|
trySwitchUser(user)
|
||||||
scope.launch {
|
|
||||||
result.await()?.let {
|
|
||||||
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
|
|
||||||
showAddUserDialog(user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAddUser = {
|
onAddUser = {
|
||||||
|
|
@ -396,14 +408,7 @@ fun SwitchUserContent(
|
||||||
},
|
},
|
||||||
onTextChange = {
|
onTextChange = {
|
||||||
if (it == user.pin) {
|
if (it == user.pin) {
|
||||||
val result = viewModel.trySwitchUser(user)
|
trySwitchUser(user)
|
||||||
scope.launch {
|
|
||||||
result.await()?.let {
|
|
||||||
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
|
|
||||||
showAddUserDialog(user)
|
|
||||||
switchUserWithPin = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.github.damontecres.wholphin.ui.setup
|
package com.github.damontecres.wholphin.ui.setup
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.WholphinApplication
|
import com.github.damontecres.wholphin.WholphinApplication
|
||||||
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
import com.github.damontecres.wholphin.data.JellyfinServerDao
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
|
|
@ -19,6 +21,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
@ -46,6 +49,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
class SwitchUserViewModel
|
class SwitchUserViewModel
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
@param:ApplicationContext private val context: Context,
|
||||||
val jellyfin: Jellyfin,
|
val jellyfin: Jellyfin,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
val serverDao: JellyfinServerDao,
|
val serverDao: JellyfinServerDao,
|
||||||
|
|
@ -124,21 +128,38 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trySwitchUser(user: JellyfinUser): Deferred<String?> =
|
fun trySwitchUser(user: JellyfinUser): Deferred<SwitchUserResult> =
|
||||||
viewModelScope.async(WholphinDispatchers.IO) {
|
viewModelScope.async(WholphinDispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val current = serverRepository.changeUser(server, user)
|
val current = serverRepository.changeUser(server, user)
|
||||||
setupNavigationManager.navigateTo(SetupDestination.AppContent(current))
|
setupNavigationManager.navigateTo(SetupDestination.AppContent(current))
|
||||||
null
|
SwitchUserResult.Success
|
||||||
} catch (ex: InvalidStatusException) {
|
} catch (ex: InvalidStatusException) {
|
||||||
if (ex.status == 401) {
|
when (ex.status) {
|
||||||
"Credentials expired, please login in again"
|
401 -> {
|
||||||
} else {
|
Timber.w(ex, "Error switching user 401")
|
||||||
ex.localizedMessage
|
SwitchUserResult.Error(
|
||||||
|
context.getString(R.string.login_credentials_expired),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
403 -> {
|
||||||
|
Timber.w(ex, "Error switching user 403")
|
||||||
|
SwitchUserResult.Error(
|
||||||
|
context.getString(R.string.login_not_authorized),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Timber.e(ex, "Error switching user")
|
||||||
|
SwitchUserResult.Error(ex.localizedMessage, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error switching user")
|
Timber.e(ex, "Error switching user")
|
||||||
ex.localizedMessage
|
SwitchUserResult.Error(ex.localizedMessage, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,3 +343,15 @@ data class SwitchUserState(
|
||||||
val serverVersion: String? = null,
|
val serverVersion: String? = null,
|
||||||
val serverVersionSupported: ServerVersionSupported = ServerVersionSupported.UNKNOWN,
|
val serverVersionSupported: ServerVersionSupported = ServerVersionSupported.UNKNOWN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of trying to switch users via [SwitchUserViewModel.trySwitchUser]
|
||||||
|
*/
|
||||||
|
sealed interface SwitchUserResult {
|
||||||
|
data object Success : SwitchUserResult
|
||||||
|
|
||||||
|
data class Error(
|
||||||
|
val errorMessage: String?,
|
||||||
|
val showLogin: Boolean,
|
||||||
|
) : SwitchUserResult
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -923,4 +923,6 @@
|
||||||
<string name="photo_albums">Photo albums</string>
|
<string name="photo_albums">Photo albums</string>
|
||||||
<string name="no_favorites_found">No favorites found!</string>
|
<string name="no_favorites_found">No favorites found!</string>
|
||||||
<string name="remove_from_playlist">Remove from playlist</string>
|
<string name="remove_from_playlist">Remove from playlist</string>
|
||||||
|
<string name="login_credentials_expired">Credentials expired, please login in again</string>
|
||||||
|
<string name="login_not_authorized">You are not authorized to access the server at this time</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue