From 446d892e8140fd7888b503f9e423af8b9edb3f9c Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Mon, 8 Jul 2024 01:48:33 +0530 Subject: [PATCH 1/8] feat: reset individual shortcut --- .../shortcuts/settings_shortcuts_cubit.dart | 37 ++++++++++++ .../shortcuts/settings_shortcuts_service.dart | 22 ++++--- .../pages/settings_shortcuts_view.dart | 60 +++++++++++++------ 3 files changed, 94 insertions(+), 25 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart index 07794e05ac213..f2c5dfe86f1b7 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart @@ -45,6 +45,11 @@ class ShortcutsCubit extends Cubit { final SettingsShortcutService service; + /// Fetches and updates shortcut data. + /// + /// This method retrieves customizable shortcuts data from ShortcutService instance + /// and updates the command shortcuts based on the provided data and current state. + /// Future fetchShortcuts() async { emit( state.copyWith( @@ -82,6 +87,7 @@ class ShortcutsCubit extends Cubit { } } + /// Saves all updated shortcuts to the Shortcut Service instance we are using. Future updateAllShortcuts() async { emit(state.copyWith(status: ShortcutsStatus.updating, error: '')); @@ -98,6 +104,7 @@ class ShortcutsCubit extends Cubit { } } + /// This method resets all shortcuts to their default commands. Future resetToDefault() async { emit(state.copyWith(status: ShortcutsStatus.updating, error: '')); @@ -114,6 +121,36 @@ class ShortcutsCubit extends Cubit { } } + /// Resets an individual shortcut to its default shortcut command. + /// Takes in the shortcut to reset. + Future resetIndividualShortcut(CommandShortcutEvent shortcut) async { + emit(state.copyWith(status: ShortcutsStatus.updating, error: '')); + + try { + // If no shortcut is found in the `defaultCommandShortcutEvents` then + // it will throw an error which will be handled by our catch block. + final defaultShortcut = defaultCommandShortcutEvents.firstWhere( + (el) => el.key == shortcut.key && el.handler == shortcut.handler, + ); + + // only update the shortcut if it is overidden + if (defaultShortcut.command != shortcut.command) { + shortcut.updateCommand(command: defaultShortcut.command); + await service.saveAllShortcuts(state.commandShortcutEvents); + } + + emit(state.copyWith(status: ShortcutsStatus.success, error: '')); + } catch (e) { + emit( + state.copyWith( + status: ShortcutsStatus.failure, + // TODO: replace this string with the correct localized string. + error: LocaleKeys.settings_shortcutsPage_couldNotSaveErrorMsg.tr(), + ), + ); + } + } + /// Checks if the new command is conflicting with other shortcut /// We also check using the key, whether this command is a codeblock /// shortcut, if so we only check a conflict with other codeblock shortcut. diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_service.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_service.dart index 35809ac585578..b88156ceaf50b 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_service.dart @@ -25,7 +25,8 @@ class SettingsShortcutService { late final File _file; final _initCompleter = Completer(); - /// Takes in commandShortcuts as an input and saves them to the shortcuts.JSON file. + /// Takes in commandShortcuts as an input and saves them to the + /// `shortcuts.JSON` file. Future saveAllShortcuts( List commandShortcuts, ) async { @@ -39,9 +40,11 @@ class SettingsShortcutService { ); } - /// Checks the file for saved shortcuts. If shortcuts do NOT exist then returns - /// an empty list. If shortcuts exist - /// then calls an utility method i.e getShortcutsFromJson which returns the saved shortcuts. + /// Checks the file for saved shortcuts. + /// If shortcuts do NOT exist then returns an empty list. + /// If shortcuts exists, then calls an utility method: + /// + /// `getShortcutsFromJson` - which returns the saved shortcuts. Future> getCustomizeShortcuts() async { await _initCompleter.future; final shortcutsInJson = await _file.readAsString(); @@ -53,8 +56,10 @@ class SettingsShortcutService { } } - /// Extracts shortcuts from the saved json file. The shortcuts in the saved file consist of [List]. - /// This list needs to be converted to List. This function is intended to facilitate the same. + /// Extracts shortcuts from the saved json file. + /// The shortcuts in the saved file consist of [List]. + /// This list needs to be converted to List. + /// This function is intended to facilitate the same. List getShortcutsFromJson(String savedJson) { final shortcuts = EditorShortcuts.fromJson(jsonDecode(savedJson)); return shortcuts.commandShortcuts; @@ -77,13 +82,14 @@ class SettingsShortcutService { await saveAllShortcuts(defaultCommandShortcutEvents); } - // Accesses the shortcuts.json file within the default AppFlowy Document Directory or creates a new file if it already doesn't exist. + /// Accesses the `shortcuts.JSON` file within the default AppFlowy Document Directory + /// or creates a new file if it already doesn't exist. Future _initializeService(File? file) async { _file = file ?? await _defaultShortcutFile(); _initCompleter.complete(); } - //returns the default file for storing shortcuts + /// returns the default file for storing shortcuts. Future _defaultShortcutFile() async { final path = await getIt().getPath(); return File( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 972e818b55233..095ab2f09c005 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -192,6 +192,7 @@ class _ResetButton extends StatelessWidget { horizontal: 6, ), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ const FlowySvg( FlowySvgs.restore_s, @@ -332,6 +333,10 @@ class _ShortcutSettingTileState extends State { _finishEditing(); } + void _resetIndividualCommand(CommandShortcutEvent shortcut) { + context.read().resetIndividualShortcut(shortcut); + } + @override void dispose() { focusNode.dispose(); @@ -395,25 +400,46 @@ class _ShortcutSettingTileState extends State { ], const Spacer(), if (isHovering) - GestureDetector( - onTap: () { - if (widget.canStartEditing()) { - setState(() { - widget.onStartEditing(); - isEditing = true; - }); - } - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: FlowyTooltip( - message: LocaleKeys.settings_shortcutsPage_editTooltip.tr(), - child: const FlowySvg( - FlowySvgs.edit_s, - size: Size.square(16), + Row( + children: [ + GestureDetector( + onTap: () { + if (widget.canStartEditing()) { + setState(() { + widget.onStartEditing(); + isEditing = true; + }); + } + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: FlowyTooltip( + message: + LocaleKeys.settings_shortcutsPage_editTooltip.tr(), + child: const FlowySvg( + FlowySvgs.edit_s, + size: Size.square(16), + ), + ), ), ), - ), + const HSpace(16), + GestureDetector( + onTap: () => _resetIndividualCommand(widget.command), + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: FlowyTooltip( + message: + //TODO: replace the localized string with the correct one + LocaleKeys.settings_shortcutsPage_editTooltip.tr(), + child: const FlowySvg( + FlowySvgs.restore_s, + size: Size.square(16), + ), + ), + ), + ), + ], ), const HSpace(8), ], From a1ee51d054a146d26195cfe26f373becdd1751be Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Mon, 15 Jul 2024 22:05:29 +0530 Subject: [PATCH 2/8] test: reset individual shortcut --- .../settings/shortcuts_settings_test.dart | 84 +++++++++++++++++ .../shortcuts_test/shortcuts_cubit_test.dart | 92 +++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart index fb3383a21886c..1d09800ea347e 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart @@ -1,3 +1,4 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -86,5 +87,88 @@ void main() { '', ); }); + + testWidgets('can reset an individual shortcut', (tester) async { + // In order to reset a shortcut, we must first override it. + + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + await tester.openSettings(); + await tester.openSettingsPage(SettingsPage.shortcuts); + await tester.pumpAndSettle(); + + final backspaceCmdText = + LocaleKeys.settings_shortcutsPage_keybindings_backspace.tr(); + final defaultBackspaceCmd = backspaceCommand.command; + + // Input "Delete" into the search field + await tester.enterText(find.byType(TextField), backspaceCmdText); + await tester.pumpAndSettle(); + + await tester.hoverOnWidget( + find.descendant( + of: find.byType(ShortcutSettingTile), + matching: find.text(backspaceCmdText), + ), + onHover: () async { + await tester.tap(find.byFlowySvg(FlowySvgs.edit_s)); + await tester.pumpAndSettle(); + + await FlowyTestKeyboard.simulateKeyDownEvent( + [ + LogicalKeyboardKey.delete, + LogicalKeyboardKey.alt, + LogicalKeyboardKey.enter, + ], + tester: tester, + ); + await tester.pumpAndSettle(); + }, + ); + + // We expect to see conflict dialog + expect( + find.text( + LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(), + ), + findsNothing, + ); + + // We expect the first ShortcutSettingTile to have one + // [KeyBadge] with `delete+alt` label + final first = tester.widget(find.byType(ShortcutSettingTile).first) + as ShortcutSettingTile; + expect( + first.command.command, + 'alt+$defaultBackspaceCmd', + ); + + // hover on the ShortcutSettingTile and click the restore button + await tester.hoverOnWidget( + find.descendant( + of: find.byType(ShortcutSettingTile), + matching: find.text(backspaceCmdText), + ), + onHover: () async { + await tester.tap( + find.descendant( + of: find.byType(ShortcutSettingTile).first, + matching: find.byFlowySvg(FlowySvgs.restore_s), + ), + ); + await tester.pumpAndSettle(); + }, + ); + + // We expect the first ShortcutSettingTile to have one + // [KeyBadge] with `delete` label + final reseted = tester.widget(find.byType(ShortcutSettingTile).first) + as ShortcutSettingTile; + expect( + reseted.command.command, + defaultBackspaceCmd, + ); + }); }); } diff --git a/frontend/appflowy_flutter/test/bloc_test/shortcuts_test/shortcuts_cubit_test.dart b/frontend/appflowy_flutter/test/bloc_test/shortcuts_test/shortcuts_cubit_test.dart index 1b896cbd3dd3d..9e99912162390 100644 --- a/frontend/appflowy_flutter/test/bloc_test/shortcuts_test/shortcuts_cubit_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/shortcuts_test/shortcuts_cubit_test.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; import 'package:appflowy/plugins/document/presentation/editor_page.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_cut_command.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; // ignore: depend_on_referenced_packages import 'package:mocktail/mocktail.dart'; @@ -18,6 +21,7 @@ void main() { setUp(() async { service = MockSettingsShortcutService(); + when( () => service.saveAllShortcuts(any()), ).thenAnswer((_) async => true); @@ -160,5 +164,93 @@ void main() { ], ); }); + + group('resetIndividualShortcut', () { + final dummyShortcut = CommandShortcutEvent( + key: 'dummy key event', + command: 'ctrl+alt+shift+arrow up', + handler: (_) { + return KeyEventResult.handled; + }, + getDescription: () => 'dummy key event', + ); + + blocTest( + 'does not call saveAllShortcuts() since dummyShortcut is not in defaultShortcuts', + build: () => shortcutsCubit, + act: (cubit) => cubit.resetIndividualShortcut(dummyShortcut), + verify: (_) { + verifyNever(() => service.saveAllShortcuts(any())); + }, + ); + + blocTest( + 'does not call saveAllShortcuts() when resetIndividualShortcut called redundantly', + build: () => shortcutsCubit, + // here we are using customCutCommand since it is a part of defaultShortcuts + act: (cubit) => cubit.resetIndividualShortcut(customCutCommand), + verify: (_) { + verifyNever(() => service.saveAllShortcuts(any())); + }, + ); + + blocTest( + 'calls saveAllShortcuts() once for shortcuts in defaultShortcuts', + build: () => shortcutsCubit, + // here we are using customCutCommand since it is a part of defaultShortcuts + // we have to override it, inorder to reset it. + act: (cubit) { + customCutCommand.updateCommand(command: 'ctrl+alt+shift+x'); + cubit.resetIndividualShortcut(customCutCommand); + }, + verify: (_) { + verify(() => service.saveAllShortcuts(any())).called(1); + }, + ); + + blocTest( + 'emits [updating, failure] when saveAllShortcuts() throws', + setUp: () { + when( + () => service.saveAllShortcuts(any()), + ).thenThrow(Exception('oops')); + }, + build: () => shortcutsCubit, + act: (cubit) { + customCutCommand.updateCommand(command: 'ctrl+alt+shift+x'); + cubit.resetIndividualShortcut(customCutCommand); + }, + expect: () => [ + const ShortcutsState(status: ShortcutsStatus.updating), + isA() + .having((w) => w.status, 'status', ShortcutsStatus.failure), + ], + ); + + blocTest( + 'emits [updating, failure] when shortcut not found in defaultShortcuts', + build: () => shortcutsCubit, + act: (cubit) => cubit.resetIndividualShortcut(dummyShortcut), + expect: () => [ + const ShortcutsState(status: ShortcutsStatus.updating), + isA() + .having((w) => w.status, 'status', ShortcutsStatus.failure), + ], + ); + + blocTest( + 'emits [updating, success] when succesfully updates shortcut', + build: () => shortcutsCubit, + act: (cubit) { + customCutCommand.updateCommand(command: 'ctrl+alt+shift+x'); + cubit.resetIndividualShortcut(customCutCommand); + }, + expect: () => [ + const ShortcutsState(status: ShortcutsStatus.updating), + isA() + .having((w) => w.status, 'status', ShortcutsStatus.success), + ], + ); + }); }); } From 3ec3af633dc94be974f4f1400833066b1ab0fa53 Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Wed, 17 Jul 2024 21:45:48 +0530 Subject: [PATCH 3/8] chore: fix the integration test --- .../settings/shortcuts_settings_test.dart | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart index 1d09800ea347e..340c14cf17cdb 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/settings/shortcuts_settings_test.dart @@ -98,27 +98,28 @@ void main() { await tester.openSettingsPage(SettingsPage.shortcuts); await tester.pumpAndSettle(); - final backspaceCmdText = - LocaleKeys.settings_shortcutsPage_keybindings_backspace.tr(); - final defaultBackspaceCmd = backspaceCommand.command; + final pageUpCmdText = + LocaleKeys.settings_shortcutsPage_keybindings_pageUp.tr(); + final defaultPageUpCmd = pageUpCommand.command; - // Input "Delete" into the search field - await tester.enterText(find.byType(TextField), backspaceCmdText); + // Input "Page Up text" into the search field + // This test works because we only have one input field on the shortcuts page. + await tester.enterText(find.byType(TextField), pageUpCmdText); await tester.pumpAndSettle(); await tester.hoverOnWidget( find.descendant( of: find.byType(ShortcutSettingTile), - matching: find.text(backspaceCmdText), + matching: find.text(pageUpCmdText), ), onHover: () async { + // changing the shortcut await tester.tap(find.byFlowySvg(FlowySvgs.edit_s)); await tester.pumpAndSettle(); await FlowyTestKeyboard.simulateKeyDownEvent( [ - LogicalKeyboardKey.delete, - LogicalKeyboardKey.alt, + LogicalKeyboardKey.backquote, LogicalKeyboardKey.enter, ], tester: tester, @@ -127,28 +128,21 @@ void main() { }, ); - // We expect to see conflict dialog - expect( - find.text( - LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(), - ), - findsNothing, - ); - // We expect the first ShortcutSettingTile to have one - // [KeyBadge] with `delete+alt` label - final first = tester.widget(find.byType(ShortcutSettingTile).first) + // [KeyBadge] with `backquote` label + // which will confirm that we have changed the command + final theOnlyTile = tester.widget(find.byType(ShortcutSettingTile).first) as ShortcutSettingTile; expect( - first.command.command, - 'alt+$defaultBackspaceCmd', + theOnlyTile.command.command, + 'backquote', ); // hover on the ShortcutSettingTile and click the restore button await tester.hoverOnWidget( find.descendant( of: find.byType(ShortcutSettingTile), - matching: find.text(backspaceCmdText), + matching: find.text(pageUpCmdText), ), onHover: () async { await tester.tap( @@ -162,12 +156,10 @@ void main() { ); // We expect the first ShortcutSettingTile to have one - // [KeyBadge] with `delete` label - final reseted = tester.widget(find.byType(ShortcutSettingTile).first) - as ShortcutSettingTile; + // [KeyBadge] with `page up` label expect( - reseted.command.command, - defaultBackspaceCmd, + theOnlyTile.command.command, + defaultPageUpCmd, ); }); }); From 92311b3ae1f2b96ae91d3342b8b9f6722e76f2e3 Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Wed, 17 Jul 2024 23:52:25 +0530 Subject: [PATCH 4/8] chore: add appropriate localized strings --- .../settings/shortcuts/settings_shortcuts_cubit.dart | 8 +++++--- .../settings/pages/settings_shortcuts_view.dart | 6 +++--- frontend/resources/translations/en.json | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart index f2c5dfe86f1b7..410e0628deddf 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart @@ -115,7 +115,9 @@ class ShortcutsCubit extends Cubit { emit( state.copyWith( status: ShortcutsStatus.failure, - error: LocaleKeys.settings_shortcutsPage_couldNotSaveErrorMsg.tr(), + error: LocaleKeys + .settings_shortcutsPage_couldNotResetShortcutsErrorMsg + .tr(), ), ); } @@ -144,8 +146,8 @@ class ShortcutsCubit extends Cubit { emit( state.copyWith( status: ShortcutsStatus.failure, - // TODO: replace this string with the correct localized string. - error: LocaleKeys.settings_shortcutsPage_couldNotSaveErrorMsg.tr(), + error: LocaleKeys.settings_shortcutsPage_couldNotResetSingleErrorMsg + .tr(), ), ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 095ab2f09c005..241af6cfb9e42 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -429,9 +429,9 @@ class _ShortcutSettingTileState extends State { child: MouseRegion( cursor: SystemMouseCursors.click, child: FlowyTooltip( - message: - //TODO: replace the localized string with the correct one - LocaleKeys.settings_shortcutsPage_editTooltip.tr(), + message: LocaleKeys + .settings_shortcutsPage_resetSingleTooltip + .tr(), child: const FlowySvg( FlowySvgs.restore_s, size: Size.square(16), diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 9523d56620903..a24e78b206884 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -531,6 +531,7 @@ "confirmLabel": "Continue" }, "editTooltip": "Press to start editing the keybinding", + "resetSingleTooltip":"Reset this shortcut", "keybindings": { "toggleToDoList": "Toggle to do list", "insertNewParagraphInCodeblock": "Insert new paragraph", @@ -614,7 +615,9 @@ "textAlignRight": "Align text to the right" }, "couldNotLoadErrorMsg": "Could not load shortcuts, Try again", - "couldNotSaveErrorMsg": "Could not save shortcuts, Try again" + "couldNotSaveErrorMsg": "Could not save shortcuts, Try again", + "couldNotResetSingleErrorMsg": "Could not reset this shortcut, Try again", + "couldNotResetShortcutsErrorMsg": "Could not reset all shortcuts, Try again" }, "aiPage": { "title": "AI Settings", From ead32f2a786666c42930375b42a5b55cc76e1891 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen Date: Mon, 5 Aug 2024 18:08:44 +0200 Subject: [PATCH 5/8] feat: disable reset shortcut when already default --- .../pages/settings_shortcuts_view.dart | 107 +++++++++++------- frontend/resources/translations/en.json | 3 +- frontend/rust-lib/Cargo.lock | 18 ++- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 241af6cfb9e42..1d4a6b381f107 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_page.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/string_extension.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_copy_command.dart'; @@ -337,6 +338,14 @@ class _ShortcutSettingTileState extends State { context.read().resetIndividualShortcut(shortcut); } + bool canResetCommand(CommandShortcutEvent shortcut) { + final defaultShortcut = defaultCommandShortcutEvents.firstWhere( + (el) => el.key == shortcut.key && el.handler == shortcut.handler, + ); + + return defaultShortcut.command != shortcut.command; + } + @override void dispose() { focusNode.dispose(); @@ -389,49 +398,59 @@ class _ShortcutSettingTileState extends State { ); } - Widget _renderKeybindings(bool isHovering) => Row( - children: [ - if (widget.command.keybindings.isNotEmpty) ...[ - ..._toParts(widget.command.keybindings.first).map( - (key) => KeyBadge(keyLabel: key), - ), - ] else ...[ - const SizedBox(height: 24), - ], - const Spacer(), - if (isHovering) - Row( - children: [ - GestureDetector( - onTap: () { - if (widget.canStartEditing()) { - setState(() { - widget.onStartEditing(); - isEditing = true; - }); - } - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: FlowyTooltip( - message: - LocaleKeys.settings_shortcutsPage_editTooltip.tr(), - child: const FlowySvg( - FlowySvgs.edit_s, - size: Size.square(16), - ), + Widget _renderKeybindings(bool isHovering) { + final canReset = canResetCommand(widget.command); + + return Row( + children: [ + if (widget.command.keybindings.isNotEmpty) ...[ + ..._toParts(widget.command.keybindings.first).map( + (key) => KeyBadge(keyLabel: key), + ), + ] else ...[ + const SizedBox(height: 24), + ], + const Spacer(), + if (isHovering) + Row( + children: [ + GestureDetector( + onTap: () { + if (widget.canStartEditing()) { + setState(() { + widget.onStartEditing(); + isEditing = true; + }); + } + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: FlowyTooltip( + message: LocaleKeys.settings_shortcutsPage_editTooltip.tr(), + child: const FlowySvg( + FlowySvgs.edit_s, + size: Size.square(16), ), ), ), - const HSpace(16), - GestureDetector( - onTap: () => _resetIndividualCommand(widget.command), + ), + const HSpace(16), + Opacity( + opacity: canReset ? 1 : 0.5, + child: GestureDetector( + onTap: canReset + ? () => _resetIndividualCommand(widget.command) + : null, child: MouseRegion( - cursor: SystemMouseCursors.click, + cursor: + canReset ? SystemMouseCursors.click : MouseCursor.defer, child: FlowyTooltip( - message: LocaleKeys - .settings_shortcutsPage_resetSingleTooltip - .tr(), + message: canReset + ? LocaleKeys.settings_shortcutsPage_resetSingleTooltip + .tr() + : LocaleKeys + .settings_shortcutsPage_unavailableResetSingleTooltip + .tr(), child: const FlowySvg( FlowySvgs.restore_s, size: Size.square(16), @@ -439,11 +458,13 @@ class _ShortcutSettingTileState extends State { ), ), ), - ], - ), - const HSpace(8), - ], - ); + ), + ], + ), + const HSpace(8), + ], + ); + } Widget _renderKeybindEditor() => TapRegion( onTapOutside: canClickOutside ? null : (_) => _finishEditing(), diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 1143730f50b45..de8a6494c857e 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -560,7 +560,8 @@ "confirmLabel": "Continue" }, "editTooltip": "Press to start editing the keybinding", - "resetSingleTooltip":"Reset this shortcut", + "unavailableResetSingleTooltip": "This shortcut is already the default", + "resetSingleTooltip": "Reset this shortcut", "keybindings": { "toggleToDoList": "Toggle to do list", "insertNewParagraphInCodeblock": "Insert new paragraph", diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 24b838922d649..b0c640802dfef 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1255,7 +1255,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -4065,7 +4065,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -4085,6 +4085,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -4152,6 +4153,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "phf_shared" version = "0.8.0" From 3cd5fe44c8648b381cc5ea39d6016abcceb58943 Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Sat, 24 Aug 2024 16:11:02 +0530 Subject: [PATCH 6/8] refactor: extract edit and reset btns as widgets --- .../pages/settings_shortcuts_view.dart | 98 ++++++++++++------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 042559360eabd..4bbecde1e29d4 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -413,8 +413,8 @@ class _ShortcutSettingTileState extends State { if (isHovering) Row( children: [ - GestureDetector( - onTap: () { + EditShortcutBtn( + onEdit: () { if (widget.canStartEditing()) { setState(() { widget.onStartEditing(); @@ -422,41 +422,11 @@ class _ShortcutSettingTileState extends State { }); } }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: FlowyTooltip( - message: LocaleKeys.settings_shortcutsPage_editTooltip.tr(), - child: const FlowySvg( - FlowySvgs.edit_s, - size: Size.square(16), - ), - ), - ), ), const HSpace(16), - Opacity( - opacity: canReset ? 1 : 0.5, - child: GestureDetector( - onTap: canReset - ? () => _resetIndividualCommand(widget.command) - : null, - child: MouseRegion( - cursor: - canReset ? SystemMouseCursors.click : MouseCursor.defer, - child: FlowyTooltip( - message: canReset - ? LocaleKeys.settings_shortcutsPage_resetSingleTooltip - .tr() - : LocaleKeys - .settings_shortcutsPage_unavailableResetSingleTooltip - .tr(), - child: const FlowySvg( - FlowySvgs.restore_s, - size: Size.square(16), - ), - ), - ), - ), + ResetShortcutBtn( + onReset: () => _resetIndividualCommand(widget.command), + canReset: canReset, ), ], ), @@ -507,6 +477,64 @@ class _ShortcutSettingTileState extends State { } } +class EditShortcutBtn extends StatelessWidget { + const EditShortcutBtn({super.key, required this.onEdit}); + + final VoidCallback onEdit; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onEdit, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: FlowyTooltip( + message: LocaleKeys.settings_shortcutsPage_editTooltip.tr(), + child: const FlowySvg( + FlowySvgs.edit_s, + size: Size.square(16), + ), + ), + ), + ); + } +} + +class ResetShortcutBtn extends StatelessWidget { + const ResetShortcutBtn({ + super.key, + required this.onReset, + required this.canReset, + }); + + final bool canReset; + final VoidCallback onReset; + + @override + Widget build(BuildContext context) { + return Opacity( + opacity: canReset ? 1 : 0.5, + child: GestureDetector( + onTap: canReset ? onReset : null, + child: MouseRegion( + cursor: canReset ? SystemMouseCursors.click : MouseCursor.defer, + child: FlowyTooltip( + message: canReset + ? LocaleKeys.settings_shortcutsPage_resetSingleTooltip.tr() + : LocaleKeys + .settings_shortcutsPage_unavailableResetSingleTooltip + .tr(), + child: const FlowySvg( + FlowySvgs.restore_s, + size: Size.square(16), + ), + ), + ), + ), + ); + } +} + @visibleForTesting class KeyBadge extends StatelessWidget { const KeyBadge({super.key, required this.keyLabel}); From 5fa42dabf326cc9061ab9c394835db3275059084 Mon Sep 17 00:00:00 2001 From: MayurSMahajan Date: Tue, 10 Sep 2024 22:02:54 +0530 Subject: [PATCH 7/8] chore: make widgets button private --- .../settings/pages/settings_shortcuts_view.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 122c543a1a70b..0ca88e14988f0 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -412,7 +412,7 @@ class _ShortcutSettingTileState extends State { if (isHovering) Row( children: [ - EditShortcutBtn( + _EditShortcutBtn( onEdit: () { if (widget.canStartEditing()) { setState(() { @@ -423,7 +423,7 @@ class _ShortcutSettingTileState extends State { }, ), const HSpace(16), - ResetShortcutBtn( + _ResetShortcutBtn( onReset: () => _resetIndividualCommand(widget.command), canReset: canReset, ), @@ -476,8 +476,8 @@ class _ShortcutSettingTileState extends State { } } -class EditShortcutBtn extends StatelessWidget { - const EditShortcutBtn({super.key, required this.onEdit}); +class _EditShortcutBtn extends StatelessWidget { + const _EditShortcutBtn({super.key, required this.onEdit}); final VoidCallback onEdit; @@ -499,8 +499,8 @@ class EditShortcutBtn extends StatelessWidget { } } -class ResetShortcutBtn extends StatelessWidget { - const ResetShortcutBtn({ +class _ResetShortcutBtn extends StatelessWidget { + const _ResetShortcutBtn({ super.key, required this.onReset, required this.canReset, From 37ccb7a93c29a0137e1ca95544d1553411bb8174 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 31 Dec 2024 09:45:04 +0800 Subject: [PATCH 8/8] fix: flutter analyze --- .../settings/pages/settings_shortcuts_view.dart | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index d01378f3693ed..24e6552d96328 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -1,12 +1,8 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/document/presentation/editor_page.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/string_extension.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_copy_command.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_cut_command.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcuts.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart'; import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart'; @@ -478,7 +474,9 @@ class _ShortcutSettingTileState extends State { } class _EditShortcutBtn extends StatelessWidget { - const _EditShortcutBtn({super.key, required this.onEdit}); + const _EditShortcutBtn({ + required this.onEdit, + }); final VoidCallback onEdit; @@ -502,7 +500,6 @@ class _EditShortcutBtn extends StatelessWidget { class _ResetShortcutBtn extends StatelessWidget { const _ResetShortcutBtn({ - super.key, required this.onReset, required this.canReset, });