Files
plezy/lib/screens/settings/keyboard_shortcuts_screen.dart
T
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00

126 lines
4.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../i18n/strings.g.dart';
import '../../models/hotkey_model.dart';
import '../../services/keyboard_shortcuts_service.dart';
import '../../utils/app_logger.dart';
import '../../services/shortcut_action.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart';
import '../../focus/focusable_button.dart';
import '../../theme/mono_tokens.dart';
import '../../widgets/focused_scroll_scaffold.dart';
import '../../widgets/focusable_list_tile.dart';
import '../../widgets/settings_section.dart';
import 'hotkey_recorder_widget.dart';
class KeyboardShortcutsScreen extends StatelessWidget {
final KeyboardShortcutsService keyboardService;
const KeyboardShortcutsScreen({super.key, required this.keyboardService});
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: keyboardService,
builder: (context, _) {
final hotkeys = keyboardService.hotkeys;
final actions = hotkeys.keys.where((action) => ShortcutAction.fromId(action)?.isSupported ?? true).toList();
return FocusedScrollScaffold(
title: Text(t.settings.keyboardShortcuts),
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(left: 16, top: 16, right: 16),
child: Align(
alignment: .centerRight,
child: FocusableButton(
onPressed: () => _resetShortcuts(context),
child: TextButton(onPressed: () => _resetShortcuts(context), child: Text(t.common.reset)),
),
),
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 8, bottom: 16),
child: SettingsGroup(
children: [
for (final action in actions)
FocusableListTile(
title: Text(keyboardService.getActionDisplayName(action)),
subtitle: Text(action),
trailing: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
border: Border.fromBorderSide(BorderSide(color: Theme.of(context).dividerColor)),
borderRadius: BorderRadius.circular(tokens(context).radiusSm),
),
child: Text(
keyboardService.formatHotkey(hotkeys[action]),
style: const TextStyle(fontFamily: 'monospace'),
),
),
onTap: () => _editHotkey(context, action, hotkeys[action]),
),
],
),
),
),
],
);
},
);
}
Future<void> _resetShortcuts(BuildContext context) async {
await keyboardService.resetToDefaults();
if (context.mounted) showSuccessSnackBar(context, t.settings.shortcutsReset);
}
void _editHotkey(BuildContext screenContext, String action, HotKey? currentHotkey) {
final actionId = action;
final actionName = keyboardService.getActionDisplayName(actionId);
showScopedDialog<void>(
context: screenContext,
builder: (BuildContext context) {
return HotKeyRecorderWidget(
actionName: actionName,
currentHotKey: currentHotkey,
onHotKeyRecorded: (newHotkey) async {
final navigator = Navigator.of(context);
if (newHotkey != null) {
final existingAction = keyboardService.getActionForHotkey(newHotkey);
if (existingAction != null && existingAction != actionId) {
navigator.pop();
showErrorSnackBar(
screenContext,
t.settings.shortcutAlreadyAssigned(action: keyboardService.getActionDisplayName(existingAction)),
);
return;
}
}
try {
await keyboardService.setHotkey(actionId, newHotkey);
} on PlatformException catch (error, stackTrace) {
appLogger.e('Failed to update keyboard shortcut', error: error, stackTrace: stackTrace);
if (screenContext.mounted) showErrorSnackBar(screenContext, t.common.error);
return;
}
if (!context.mounted) return;
navigator.pop();
if (screenContext.mounted) {
showSuccessSnackBar(screenContext, t.settings.shortcutUpdated(action: actionName));
}
},
onCancel: () => Navigator.pop(context),
);
},
);
}
}