diff --git a/app/src/test/java/org/justinvisser/wolfskin/tv/AnimeAvailabilityTest.kt b/app/src/test/java/org/justinvisser/wolfskin/tv/AnimeAvailabilityTest.kt index e5d0dfa5..35e45892 100644 --- a/app/src/test/java/org/justinvisser/wolfskin/tv/AnimeAvailabilityTest.kt +++ b/app/src/test/java/org/justinvisser/wolfskin/tv/AnimeAvailabilityTest.kt @@ -82,13 +82,18 @@ class AnimeAvailabilityTest { includeItemTypes = listOf(BaseItemKind.SERIES), limit = 1) } returns successQueryResult(titles) val libraries = LibraryReadFacade(api) val session = WolfskinSession().apply { - state = ShellState(listOf(ShellPerson(key, key, key.take(1), false, user.id.toString())), key) + state = ShellState(listOf(ShellPerson(key, key, key.take(1), count == 0, user.id.toString())), key).apply { + pickerVisible = false + focusNav(tabs.size) + } } val model = WolfskinShellViewModel(context, session, mockk(), mockk(), mockk(), mockk(relaxed = true), libraries, WolfskinSeeAllLists(), conductarr, mockk(relaxed = true)) try { requireNotNull(model.onForeground(server, user)).join() assertEquals("$key with $count Anime titles", count > 0, "anime" in session.state!!.tabs) + assertEquals("The avatar must remain focused when Anime appears or disappears", + session.state!!.tabs.size, session.state!!.navIndex) val home = HomeLibrary(destinations = mapOf("anime" to titles.map { HomeTitle(it.id.toString(), it.name.orEmpty(), "tv") }), destinationTotals = mapOf("anime" to count)) assertEquals("An empty Anime audience must not create a Home shelf", count > 0, diff --git a/docker/README.md b/docker/README.md index b1a5993d..1996dd3d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -499,6 +499,8 @@ refresh enables Anime only after a one-title Jellyfin read confirms visible seri in an accessible Anime library for the signed-in user. This runs after the live picker is published; failures retain that user's last confirmed availability. Empty Anime Home shelves remain omitted. +When availability changes, focus follows the avatar or a surviving destination; +removing Anime never leaves an out-of-range nav selection. | Screen seam | Production supplier | | --- | --- | diff --git a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/ShellState.kt b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/ShellState.kt index 4afd8415..e54c7ed8 100644 --- a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/ShellState.kt +++ b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/ShellState.kt @@ -50,14 +50,17 @@ class ShellState( var people by mutableStateOf(people) private set - /** Refresh household labels without discarding this person's navigation memory. */ + /** Refresh household data without losing valid nav focus or this person's view memory. */ fun updatePeople(value: List) { + val focusedTab = tabs.getOrNull(navIndex) + val avatarFocused = navIndex == tabs.size val selected = value.firstOrNull { it.jellyfinUserId != null && it.jellyfinUserId == person.jellyfinUserId } ?: value.firstOrNull { it.key == person.key } people = value if (selected != null) person = selected pickerIndex = people.indexOf(person).coerceAtLeast(0) preparePersonMemory() + navIndex = if (avatarFocused) tabs.size else tabs.indexOf(focusedTab).takeIf { it >= 0 } ?: currentTab } var person by mutableStateOf(people.firstOrNull { it.key == initialPerson } ?: people.first()) private set