Conversation
Both panels sit on top of the grid they were opened from, and a 5-way press at their edge walked straight out of them: up from the first row, or left from anywhere, landed on the poster grid or the A to Z strip while the panel was still open. Both were declared `restrict: 'self-only'`, which never took effect. SpotlightContainerDecorator reads restrict from its `spotlightRestrict` prop rather than from this config object, and useSpotlightContainer then applies that prop with setProps, so the config's value is overwritten by the prop default of self-first. self-first is what was running: prefer what is inside, but leave when nothing lies that way. `leaveFor` is what the rest of the app holds panels with, and it does reach the container. Twenty-eight of them already carry it, the settings screens among them, which is why those hold focus and these two did not. Both now share one config so they cannot drift apart again. The inert `restrict` is left in place to match the containers that already pair the two. Related to Moonfin-Client#440 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ Build SuccessfulAll platform builds and the test suite passed. You can download the artifacts below.
|
Holding the panel against 5-way was not enough: picking a filter still threw focus out to the grid, because something else was reaching in and moving it. useStartLetter hands focus to the grid once the narrowed list has settled, which means watching the rebuilt list to know when that is. The trouble is the list rebuilds for anything — a filter toggled in the panel, a search typed in the header — so the effect answered those too and pulled focus to the grid while the viewer was still working in the panel. It fired whenever items.length or isLoading changed, with nothing to say whether the letter had anything to do with it. It now remembers which letter it last moved for and stays put until that changes, so a rebuild it did not ask for goes by unnoticed. Clearing the letter forgets it, so picking the same one again still counts as a fresh pick. Tests cover the narrowing and the toggle as well, since the hook had none. The one that matters is the rebuild under an unchanged letter: it fails without this and passes with it. Related to Moonfin-Client#440 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Second commit: something else was reaching in and moving focusThe first commit holds the panel against 5-way, and on a set that part works. But picking a filter still threw focus out to the grid — a different bug wearing the same clothes.
useEffect(() => {
if (!startLetter || items.length === 0 || isLoading) return undefined;
const id = setTimeout(() => Spotlight.focus(gridSpotlightId), 100);
return () => clearTimeout(id);
}, [startLetter, items.length, isLoading, gridSpotlightId]);The list rebuilds for anything — a filter toggled in the panel, a search typed in the header — so the effect answered those too, with nothing in it to say whether the letter had anything to do with the rebuild. Any reload while a letter was active pulled focus to the grid, out of whatever panel the viewer was working in. It now remembers which letter it last moved for and stays put until that changes. Clearing the letter forgets it, so picking the same one again still counts as a fresh pick.
TestsThe hook had none, so this adds six covering the narrowing and the same-letter toggle as well. The one that matters is "leaves focus alone when the list rebuilds under the same letter" — verified it fails without this commit and passes with it, with the other five passing either way. Full suite: 1641 passed, 3 failed — the pre-existing locale-sensitive ones. Scope
Still unverified on a set: the reporter confirmed the first commit works and found this while testing it. This second one wants the same check. 🤖 Generated with Claude Code |
Picking a filter with no letter selected still threw focus to the grid, so the alphabet strip was not the only thing reaching in. This is the one that fires every time. The grid takes focus once the library has something to show, guarded by a flag so it only happens on arrival. The reload effect clears that flag, and it runs for every filter in its dependency list — tags, genres, years, languages, all of them. So each pick cleared the flag, the reload came back, and the grid took focus while the panel was still open and being used. The grid now waits while either panel is on screen. A panel up is the viewer still choosing; the handlers that close a panel already hand focus to the grid themselves, so nothing is lost by leaving it to them. Sits below the panel state it now reads, rather than above it where the dependency list would have been evaluated before those were declared. Related to Moonfin-Client#440 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pull Request
Summary
The Sort & Filter panel and the settings panel beside it let 5-way walk straight out of them: Up from the first row, or Left from anywhere, landed on the poster grid or the A–Z letter strip while the panel was still open.
Scoped deliberately to these two panels so the approach can be checked on a real set before anything wider. See #440 for the full analysis.
Related Issues
Type of Change
Changes Made
Both panels were declared
restrict: 'self-only', which never took effect:SpotlightContainerDecoratorreadsrestrictfrom itsspotlightRestrictprop, not from this config object, anduseSpotlightContainerthen applies that prop withsetProps({…, restrict})— overwriting whatever the config carried. The prop defaults to'self-first', andself-firstis exactly the reported behaviour: prefer what is inside, but leave when nothing lies that way.enterTois unaffected, which is why the panels behave correctly in every other respect and this went unnoticed.The fix is the one the rest of the app already uses.
leaveFordoes reach the container, and 28 containers already carry it, the settings screens among them — which is precisely why those hold focus and these two did not:Both Library panels now share one config object so they cannot drift apart again. The inert
restrictis kept to match the containers that already pair the two.Platform
Testing
Manually tested by @Kisnov on a Samsung TU43DU7105KXXC running Tizen 9: 5-way no longer walks out of either panel, and picking a filter leaves focus where it was, with and without a letter active on the alphabet strip.
enact lintclean. Full suite 1641 passed, 3 failed — those 3 are the pre-existing locale-sensitive tests inpersonCreditsandseerrBadgesthat fail identically on a clean tree.Test Steps
Screenshots (if applicable)
Not applicable — no visual change; this is 5-way containment only.
Checklist
enact lintcleanNot included
52 container declarations do not carry
leaveFor, but most of them should not — rows, tab bars, the nav bar and the grid are all meant to let focus out. Only panels and overlays that sit on top of something want it. Deciding which of the rest qualify is a judgement call per container and belongs in its own pass, not bundled here.🤖 Generated with Claude Code