mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Fix some crashes due to bad UI state (#617)
## Description Fixes two possible crashes: - When saving view options for a favorite page tab - When there are no results on a grid page/tab
This commit is contained in:
parent
0958578082
commit
83a543ad7d
4 changed files with 203 additions and 186 deletions
|
|
@ -169,6 +169,7 @@ class CollectionFolderViewModel
|
|||
context.getString(R.string.error_loading_collection, itemId),
|
||||
) + Dispatchers.IO,
|
||||
) {
|
||||
super.itemId = itemId
|
||||
itemId.toUUIDOrNull()?.let {
|
||||
fetchItem(it)
|
||||
}
|
||||
|
|
@ -206,7 +207,7 @@ class CollectionFolderViewModel
|
|||
) {
|
||||
if (collectionFilter.useSavedLibraryDisplayInfo) {
|
||||
serverRepository.currentUser.value?.let { user ->
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
val libraryDisplayInfo =
|
||||
LibraryDisplayInfo(
|
||||
userId = user.rowId,
|
||||
|
|
|
|||
|
|
@ -196,210 +196,224 @@ fun <T : CardGridItem> CardGrid(
|
|||
}
|
||||
}
|
||||
|
||||
var longPressing by remember { mutableStateOf(false) }
|
||||
Row(
|
||||
// horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.onKeyEvent {
|
||||
if (DEBUG) Timber.d("onKeyEvent: ${it.nativeKeyEvent}")
|
||||
if (useBackToJump && it.key == Key.Back && it.nativeKeyEvent.isLongPress) {
|
||||
longPressing = true
|
||||
val newPosition = previouslyFocusedIndex
|
||||
if (DEBUG) Timber.d("Back long pressed: newPosition=$newPosition")
|
||||
if (newPosition > 0) {
|
||||
focusOn(newPosition)
|
||||
scope.launch(ExceptionHandler()) {
|
||||
gridState.scrollToItem(newPosition, -columns)
|
||||
firstFocus.tryRequestFocus()
|
||||
}
|
||||
}
|
||||
return@onKeyEvent true
|
||||
} else if (it.type == KeyEventType.KeyUp) {
|
||||
if (longPressing && it.key == Key.Back) {
|
||||
longPressing = false
|
||||
return@onKeyEvent true
|
||||
}
|
||||
longPressing = false
|
||||
}
|
||||
if (it.type != KeyEventType.KeyUp) {
|
||||
return@onKeyEvent false
|
||||
} else if (useBackToJump && it.key == Key.Back && focusedIndex > 0) {
|
||||
jumpToTop()
|
||||
return@onKeyEvent true
|
||||
} else if (isPlayKeyUp(it)) {
|
||||
val item = pager.getOrNull(focusedIndex)
|
||||
if (item?.playable == true) {
|
||||
Timber.v("Clicked play on ${item.id}")
|
||||
onClickPlay.invoke(focusedIndex, item)
|
||||
}
|
||||
return@onKeyEvent true
|
||||
} else if (useJumpRemoteButtons && isForwardButton(it)) {
|
||||
jump(jump1)
|
||||
return@onKeyEvent true
|
||||
} else if (useJumpRemoteButtons && isBackwardButton(it)) {
|
||||
jump(-jump1)
|
||||
return@onKeyEvent true
|
||||
} else {
|
||||
return@onKeyEvent false
|
||||
}
|
||||
},
|
||||
) {
|
||||
if (showJumpButtons && pager.isNotEmpty()) {
|
||||
JumpButtons(
|
||||
jump1 = jump1,
|
||||
jump2 = jump2,
|
||||
jumpClick = { jump(it) },
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
if (pager.isEmpty()) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_results),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columns),
|
||||
horizontalArrangement = Arrangement.spacedBy(spacing),
|
||||
verticalArrangement = Arrangement.spacedBy(spacing),
|
||||
state = gridState,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.focusGroup()
|
||||
.focusRestorer(firstFocus)
|
||||
.focusProperties {
|
||||
onExit = {
|
||||
// Leaving the grid, so "forget" the position
|
||||
// focusedIndex = -1
|
||||
}
|
||||
onEnter = {
|
||||
if (focusedIndex < 0 && gridState.firstVisibleItemIndex <= startPosition) {
|
||||
focusedIndex = startPosition
|
||||
} else {
|
||||
var longPressing by remember { mutableStateOf(false) }
|
||||
Row(
|
||||
// horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.onKeyEvent {
|
||||
if (DEBUG) Timber.d("onKeyEvent: ${it.nativeKeyEvent}")
|
||||
if (useBackToJump && it.key == Key.Back && it.nativeKeyEvent.isLongPress) {
|
||||
longPressing = true
|
||||
val newPosition = previouslyFocusedIndex
|
||||
if (DEBUG) Timber.d("Back long pressed: newPosition=$newPosition")
|
||||
if (newPosition > 0) {
|
||||
focusOn(newPosition)
|
||||
scope.launch(ExceptionHandler()) {
|
||||
gridState.scrollToItem(newPosition, -columns)
|
||||
firstFocus.tryRequestFocus()
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
items(pager.size) { index ->
|
||||
val mod =
|
||||
if ((index == focusedIndex) or (focusedIndex < 0 && index == 0)) {
|
||||
if (DEBUG) Timber.d("Adding firstFocus to focusedIndex $index")
|
||||
Modifier
|
||||
.focusRequester(firstFocus)
|
||||
.focusRequester(gridFocusRequester)
|
||||
.focusRequester(alphabetFocusRequester)
|
||||
} else {
|
||||
Modifier
|
||||
return@onKeyEvent true
|
||||
} else if (it.type == KeyEventType.KeyUp) {
|
||||
if (longPressing && it.key == Key.Back) {
|
||||
longPressing = false
|
||||
return@onKeyEvent true
|
||||
}
|
||||
longPressing = false
|
||||
}
|
||||
val item = pager[index]
|
||||
cardContent(
|
||||
item,
|
||||
{
|
||||
if (item != null) {
|
||||
focusedIndex = index
|
||||
onClickItem.invoke(index, item)
|
||||
if (it.type != KeyEventType.KeyUp) {
|
||||
return@onKeyEvent false
|
||||
} else if (useBackToJump && it.key == Key.Back && focusedIndex > 0) {
|
||||
jumpToTop()
|
||||
return@onKeyEvent true
|
||||
} else if (isPlayKeyUp(it)) {
|
||||
val item = pager.getOrNull(focusedIndex)
|
||||
if (item?.playable == true) {
|
||||
Timber.v("Clicked play on ${item.id}")
|
||||
onClickPlay.invoke(focusedIndex, item)
|
||||
}
|
||||
},
|
||||
{ if (item != null) onLongClickItem.invoke(index, item) },
|
||||
mod
|
||||
.ifElse(index == 0, Modifier.focusRequester(zeroFocus))
|
||||
.onFocusChanged { focusState ->
|
||||
if (DEBUG) {
|
||||
Timber.v(
|
||||
"$index isFocused=${focusState.isFocused}",
|
||||
)
|
||||
return@onKeyEvent true
|
||||
} else if (useJumpRemoteButtons && isForwardButton(it)) {
|
||||
jump(jump1)
|
||||
return@onKeyEvent true
|
||||
} else if (useJumpRemoteButtons && isBackwardButton(it)) {
|
||||
jump(-jump1)
|
||||
return@onKeyEvent true
|
||||
} else {
|
||||
return@onKeyEvent false
|
||||
}
|
||||
},
|
||||
) {
|
||||
if (showJumpButtons && pager.isNotEmpty()) {
|
||||
JumpButtons(
|
||||
jump1 = jump1,
|
||||
jump2 = jump2,
|
||||
jumpClick = { jump(it) },
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columns),
|
||||
horizontalArrangement = Arrangement.spacedBy(spacing),
|
||||
verticalArrangement = Arrangement.spacedBy(spacing),
|
||||
state = gridState,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.focusGroup()
|
||||
.focusRestorer(firstFocus)
|
||||
.focusProperties {
|
||||
onExit = {
|
||||
// Leaving the grid, so "forget" the position
|
||||
// focusedIndex = -1
|
||||
}
|
||||
if (focusState.isFocused) {
|
||||
// Focused, so set that up
|
||||
focusOn(index)
|
||||
positionCallback?.invoke(columns, index)
|
||||
} else if (focusedIndex == index) {
|
||||
onEnter = {
|
||||
if (focusedIndex < 0 && gridState.firstVisibleItemIndex <= startPosition) {
|
||||
focusedIndex = startPosition
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
items(pager.size) { index ->
|
||||
val mod =
|
||||
if ((index == focusedIndex) or (focusedIndex < 0 && index == 0)) {
|
||||
if (DEBUG) Timber.d("Adding firstFocus to focusedIndex $index")
|
||||
Modifier
|
||||
.focusRequester(firstFocus)
|
||||
.focusRequester(gridFocusRequester)
|
||||
.focusRequester(alphabetFocusRequester)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
val item = pager[index]
|
||||
cardContent(
|
||||
item,
|
||||
{
|
||||
if (item != null) {
|
||||
focusedIndex = index
|
||||
onClickItem.invoke(index, item)
|
||||
}
|
||||
},
|
||||
{ if (item != null) onLongClickItem.invoke(index, item) },
|
||||
mod
|
||||
.ifElse(index == 0, Modifier.focusRequester(zeroFocus))
|
||||
.onFocusChanged { focusState ->
|
||||
if (DEBUG) {
|
||||
Timber.v(
|
||||
"$index isFocused=${focusState.isFocused}",
|
||||
)
|
||||
}
|
||||
if (focusState.isFocused) {
|
||||
// Focused, so set that up
|
||||
focusOn(index)
|
||||
positionCallback?.invoke(columns, index)
|
||||
} else if (focusedIndex == index) {
|
||||
// savedFocusedIndex = index
|
||||
// // Was focused on this, so mark unfocused
|
||||
// focusedIndex = -1
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pager.isEmpty()) {
|
||||
if (pager.isEmpty()) {
|
||||
// focusedIndex = -1
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_results),
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_results),
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showFooter) {
|
||||
// Footer
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(AppColors.TransparentBlack50),
|
||||
) {
|
||||
val index = (focusedIndex + 1).takeIf { it > 0 } ?: "?"
|
||||
if (showFooter) {
|
||||
// Footer
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(AppColors.TransparentBlack50),
|
||||
) {
|
||||
val index = (focusedIndex + 1).takeIf { it > 0 } ?: "?"
|
||||
// if (focusedIndex >= 0) {
|
||||
// focusedIndex + 1
|
||||
// } else {
|
||||
// max(savedFocusedIndex, focusedIndexOnExit) + 1
|
||||
// }
|
||||
Text(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
text = "$index / ${pager.size}",
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
text = "$index / ${pager.size}",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val context = LocalContext.current
|
||||
val letters = context.getString(R.string.jump_letters)
|
||||
// Letters
|
||||
val currentLetter =
|
||||
remember(focusedIndex) {
|
||||
pager
|
||||
.getOrNull(focusedIndex)
|
||||
?.sortName
|
||||
?.first()
|
||||
?.uppercaseChar()
|
||||
?.let {
|
||||
if (it >= '0' && it <= '9') {
|
||||
'#'
|
||||
} else if (it >= 'A' && it <= 'Z') {
|
||||
it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
?: letters[0]
|
||||
}
|
||||
if (showLetterButtons && pager.isNotEmpty()) {
|
||||
AlphabetButtons(
|
||||
letters = letters,
|
||||
currentLetter = currentLetter,
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(end = 16.dp),
|
||||
// Add end padding to push away from edge
|
||||
letterClicked = { letter ->
|
||||
scope.launch(ExceptionHandler()) {
|
||||
val jumpPosition =
|
||||
withContext(Dispatchers.IO) {
|
||||
letterPosition.invoke(letter)
|
||||
val context = LocalContext.current
|
||||
val letters = context.getString(R.string.jump_letters)
|
||||
// Letters
|
||||
val currentLetter =
|
||||
remember(focusedIndex) {
|
||||
pager
|
||||
.getOrNull(focusedIndex)
|
||||
?.sortName
|
||||
?.first()
|
||||
?.uppercaseChar()
|
||||
?.let {
|
||||
if (it >= '0' && it <= '9') {
|
||||
'#'
|
||||
} else if (it >= 'A' && it <= 'Z') {
|
||||
it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
Timber.d("Alphabet jump to $jumpPosition")
|
||||
if (jumpPosition >= 0) {
|
||||
pager.getOrNull(jumpPosition)
|
||||
gridState.scrollToItem(jumpPosition)
|
||||
focusOn(jumpPosition)
|
||||
alphabetFocus = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
?: letters[0]
|
||||
}
|
||||
if (showLetterButtons && pager.isNotEmpty()) {
|
||||
AlphabetButtons(
|
||||
letters = letters,
|
||||
currentLetter = currentLetter,
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(end = 16.dp),
|
||||
// Add end padding to push away from edge
|
||||
letterClicked = { letter ->
|
||||
scope.launch(ExceptionHandler()) {
|
||||
val jumpPosition =
|
||||
withContext(Dispatchers.IO) {
|
||||
letterPosition.invoke(letter)
|
||||
}
|
||||
Timber.d("Alphabet jump to $jumpPosition")
|
||||
if (jumpPosition >= 0) {
|
||||
pager.getOrNull(jumpPosition)
|
||||
gridState.scrollToItem(jumpPosition)
|
||||
focusOn(jumpPosition)
|
||||
alphabetFocus = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ abstract class ItemViewModel(
|
|||
) : ViewModel() {
|
||||
val item = MutableLiveData<BaseItem?>(null)
|
||||
lateinit var itemId: String
|
||||
lateinit var itemUuid: UUID
|
||||
var itemUuid: UUID? = null
|
||||
|
||||
suspend fun fetchItem(itemId: UUID): BaseItem =
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
|
|||
|
|
@ -155,8 +155,10 @@ class PersonViewModel
|
|||
|
||||
fun setFavorite(favorite: Boolean) {
|
||||
viewModelScope.launchIO {
|
||||
favoriteWatchManager.setFavorite(itemUuid, favorite)
|
||||
fetchAndSetItem(itemUuid)
|
||||
itemUuid?.let {
|
||||
favoriteWatchManager.setFavorite(it, favorite)
|
||||
fetchAndSetItem(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue