fix(catalog): keep the watchlist action focusable while membership loads
Keep the action enabled and let a press retry the snapshot, as the media detail action bar already does. A disabled sole action left the detail screen with no initial D-pad focus on TV.
This commit is contained in:
@@ -289,8 +289,15 @@ class _CatalogItemDetailScreenState extends State<CatalogItemDetailScreen> {
|
||||
|
||||
Future<void> _toggleWatchlist() async {
|
||||
final source = _watchlistSource;
|
||||
if (source == null || _mutatingWatchlist) return;
|
||||
final current = _isOnWatchlist;
|
||||
if (source == null || current == null || _mutatingWatchlist) return;
|
||||
// Parity with lib/screens/media_detail/action_buttons.dart: the action
|
||||
// stays focusable while membership is unknown, and a press kicks the
|
||||
// snapshot load rather than toggling a state we haven't read yet.
|
||||
if (current == null) {
|
||||
unawaited(source.ensureWatchlistLoaded());
|
||||
return;
|
||||
}
|
||||
_mutatingWatchlist = true;
|
||||
try {
|
||||
if (current) {
|
||||
@@ -564,9 +571,7 @@ class _CatalogItemDetailScreenState extends State<CatalogItemDetailScreen> {
|
||||
tooltip: onWatchlist ?? false
|
||||
? t.explore.removeFromWatchlist
|
||||
: t.explore.addToWatchlist,
|
||||
onPressed: onWatchlist == null
|
||||
? null
|
||||
: () => unawaited(_toggleWatchlist()),
|
||||
onPressed: () => unawaited(_toggleWatchlist()),
|
||||
),
|
||||
if (_requestSource case final SeerrCatalogSource seerr)
|
||||
FocusableAction(
|
||||
|
||||
@@ -243,31 +243,24 @@ void main() {
|
||||
expect(FocusManager.instance.primaryFocus?.debugLabel, 'catalog_library_match_1');
|
||||
});
|
||||
|
||||
testWidgets('loading watchlist action cannot receive focus or activate', (tester) async {
|
||||
testWidgets('pending watchlist action keeps initial focus and its press retries the snapshot', (tester) async {
|
||||
final source = _FakeCatalogSource(watchlistLoading: true);
|
||||
await _pumpDetail(tester, source);
|
||||
|
||||
final actionBar = tester.widget<FocusableActionBar>(find.byType(FocusableActionBar));
|
||||
expect(actionBar.actions.single.onPressed, isNull);
|
||||
expect(FocusManager.instance.primaryFocus?.debugLabel, 'ActionBar[0]');
|
||||
final actionNode = tester
|
||||
.widgetList<Focus>(find.descendant(of: find.byType(FocusableActionBar), matching: find.byType(Focus)))
|
||||
.map((widget) => widget.focusNode)
|
||||
.whereType<FocusNode>()
|
||||
.singleWhere((node) => node.debugLabel == 'ActionBar[0]');
|
||||
expect(actionNode.canRequestFocus, isFalse);
|
||||
expect(actionNode.canRequestFocus, isTrue);
|
||||
|
||||
actionNode.requestFocus();
|
||||
await tester.pump();
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.select);
|
||||
expect(actionNode.hasFocus, isFalse);
|
||||
await tester.pump();
|
||||
expect(source.addToWatchlistCalls, 0);
|
||||
|
||||
source.completeWatchlistLoad();
|
||||
await tester.pump();
|
||||
final loadedActionBar = tester.widget<FocusableActionBar>(find.byType(FocusableActionBar));
|
||||
expect(loadedActionBar.actions.single.onPressed, isNotNull);
|
||||
actionNode.requestFocus();
|
||||
await tester.pump();
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.select);
|
||||
await tester.pump();
|
||||
expect(source.addToWatchlistCalls, 1);
|
||||
|
||||
Reference in New Issue
Block a user