diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart index a32abd33aa..a1f3a78f75 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart @@ -14,7 +14,6 @@ import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart'; import 'package:appflowy/plugins/database/widgets/card/card_bloc.dart'; import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/desktop_board_card_cell_style.dart'; import 'package:appflowy/plugins/database/widgets/row/row_detail.dart'; -import 'package:appflowy/plugins/shared/callback_shortcuts.dart'; import 'package:appflowy/shared/conditional_listenable_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; @@ -29,7 +28,6 @@ import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flutter/material.dart' hide Card; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:provider/provider.dart'; import '../../widgets/card/card.dart'; import '../../widgets/cell/card_cell_builder.dart'; @@ -322,67 +320,64 @@ class _BoardContentState extends State<_BoardContent> { }, ), ], - child: Provider( - create: (context) => AFCallbackShortcutsProvider(), - child: FocusScope( - autofocus: true, - child: BoardShortcutContainer( - focusScope: widget.focusScope, - child: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: AppFlowyBoard( - boardScrollController: scrollManager, - scrollController: scrollController, - controller: context.read().boardController, - groupConstraints: const BoxConstraints.tightFor(width: 256), - config: config, - leading: HiddenGroupsColumn(margin: config.groupHeaderPadding), - trailing: context - .read() - .groupingFieldType - ?.canCreateNewGroup ?? - false - ? BoardTrailing(scrollController: scrollController) - : const HSpace(40), - headerBuilder: (_, groupData) => BlocProvider.value( - value: context.read(), - child: BoardColumnHeader( - groupData: groupData, - margin: config.groupHeaderPadding, - ), + child: FocusScope( + autofocus: true, + child: BoardShortcutContainer( + focusScope: widget.focusScope, + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: AppFlowyBoard( + boardScrollController: scrollManager, + scrollController: scrollController, + controller: context.read().boardController, + groupConstraints: const BoxConstraints.tightFor(width: 256), + config: config, + leading: HiddenGroupsColumn(margin: config.groupHeaderPadding), + trailing: context + .read() + .groupingFieldType + ?.canCreateNewGroup ?? + false + ? BoardTrailing(scrollController: scrollController) + : const HSpace(40), + headerBuilder: (_, groupData) => BlocProvider.value( + value: context.read(), + child: BoardColumnHeader( + groupData: groupData, + margin: config.groupHeaderPadding, ), - footerBuilder: (_, groupData) => MultiBlocProvider( - providers: [ - BlocProvider.value( - value: context.read(), - ), - BlocProvider.value( - value: context.read(), - ), - ], - child: BoardColumnFooter( - columnData: groupData, - boardConfig: config, - scrollManager: scrollManager, + ), + footerBuilder: (_, groupData) => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: context.read(), ), + BlocProvider.value( + value: context.read(), + ), + ], + child: BoardColumnFooter( + columnData: groupData, + boardConfig: config, + scrollManager: scrollManager, ), - cardBuilder: (_, column, columnItem) => MultiBlocProvider( - key: ValueKey("board_card_${column.id}_${columnItem.id}"), - providers: [ - BlocProvider.value( - value: context.read(), - ), - BlocProvider.value( - value: context.read(), - ), - ], - child: _BoardCard( - afGroupData: column, - groupItem: columnItem as GroupItem, - boardConfig: config, - notifier: widget.focusScope, - cellBuilder: cellBuilder, + ), + cardBuilder: (_, column, columnItem) => MultiBlocProvider( + key: ValueKey("board_card_${column.id}_${columnItem.id}"), + providers: [ + BlocProvider.value( + value: context.read(), ), + BlocProvider.value( + value: context.read(), + ), + ], + child: _BoardCard( + afGroupData: column, + groupItem: columnItem as GroupItem, + boardConfig: config, + notifier: widget.focusScope, + cellBuilder: cellBuilder, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_column_header.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_column_header.dart index 53cd3c1e4f..4504563a97 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_column_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_column_header.dart @@ -3,7 +3,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/database/board/application/board_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart'; -import 'package:appflowy/plugins/shared/callback_shortcuts.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_board/appflowy_board.dart'; @@ -32,7 +31,6 @@ class BoardColumnHeader extends StatefulWidget { class _BoardColumnHeaderState extends State { final FocusNode _focusNode = FocusNode(); final FocusNode _keyboardListenerFocusNode = FocusNode(); - late final AFCallbackShortcutsProvider _shortcutsProvider; late final TextEditingController _controller = TextEditingController.fromValue( @@ -47,21 +45,15 @@ class _BoardColumnHeaderState extends State { @override void initState() { super.initState(); - _shortcutsProvider = context.read(); _focusNode.addListener(() { if (!_focusNode.hasFocus) { _saveEdit(); } }); - _keyboardListenerFocusNode.addListener(() { - _shortcutsProvider.isShortcutsEnabled.value = - !_keyboardListenerFocusNode.hasFocus; - }); } @override void dispose() { - _shortcutsProvider.isShortcutsEnabled.value = true; _focusNode.dispose(); _keyboardListenerFocusNode.dispose(); _controller.dispose(); diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_focus_scope.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_focus_scope.dart index e636ab626d..ed329904b9 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_focus_scope.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_focus_scope.dart @@ -39,14 +39,14 @@ class BoardFocusScope extends ChangeNotifier notifyListeners(); } - void focusNext() { + bool focusNext() { _deepCopy(); // if no card is focused, focus on the first card in the board if (_focusedCards.isEmpty) { _focusFirstCard(); notifyListeners(); - return; + return true; } final lastFocusedCard = _focusedCards.last; @@ -58,7 +58,7 @@ class BoardFocusScope extends ChangeNotifier if (iterable == null || iterable.isEmpty) { _focusFirstCard(); notifyListeners(); - return; + return true; } if (iterable.length == 1) { @@ -90,16 +90,18 @@ class BoardFocusScope extends ChangeNotifier } notifyListeners(); + + return true; } - void focusPrevious() { + bool focusPrevious() { _deepCopy(); // if no card is focused, focus on the last card in the board if (_focusedCards.isEmpty) { _focusLastCard(); notifyListeners(); - return; + return true; } final lastFocusedCard = _focusedCards.last; @@ -111,7 +113,7 @@ class BoardFocusScope extends ChangeNotifier if (iterable == null || iterable.isEmpty) { _focusLastCard(); notifyListeners(); - return; + return true; } if (iterable.length == 1) { @@ -143,16 +145,18 @@ class BoardFocusScope extends ChangeNotifier } notifyListeners(); + + return true; } - void adjustRangeDown() { + bool adjustRangeDown() { _deepCopy(); // if no card is focused, focus on the first card in the board if (_focusedCards.isEmpty) { _focusFirstCard(); notifyListeners(); - return; + return true; } final firstFocusedCard = _focusedCards.first; @@ -171,7 +175,7 @@ class BoardFocusScope extends ChangeNotifier if (firstGroupIndex == -1 || lastGroupIndex == -1) { _focusFirstCard(); notifyListeners(); - return; + return true; } if (firstGroupIndex < lastGroupIndex) { @@ -189,7 +193,7 @@ class BoardFocusScope extends ChangeNotifier if (firstCardIndex == -1 || lastCardIndex == -1) { _focusFirstCard(); notifyListeners(); - return; + return true; } isExpand = firstCardIndex < lastCardIndex; @@ -203,7 +207,7 @@ class BoardFocusScope extends ChangeNotifier if (groupController == null) { _focusFirstCard(); notifyListeners(); - return; + return true; } final iterable = groupController.items @@ -236,16 +240,17 @@ class BoardFocusScope extends ChangeNotifier } notifyListeners(); + return true; } - void adjustRangeUp() { + bool adjustRangeUp() { _deepCopy(); // if no card is focused, focus on the first card in the board if (_focusedCards.isEmpty) { _focusLastCard(); notifyListeners(); - return; + return true; } final firstFocusedCard = _focusedCards.first; @@ -264,7 +269,7 @@ class BoardFocusScope extends ChangeNotifier if (firstGroupIndex == -1 || lastGroupIndex == -1) { _focusLastCard(); notifyListeners(); - return; + return true; } if (firstGroupIndex < lastGroupIndex) { @@ -282,7 +287,7 @@ class BoardFocusScope extends ChangeNotifier if (firstCardIndex == -1 || lastCardIndex == -1) { _focusLastCard(); notifyListeners(); - return; + return true; } isExpand = firstCardIndex > lastCardIndex; @@ -296,7 +301,7 @@ class BoardFocusScope extends ChangeNotifier if (groupController == null) { _focusLastCard(); notifyListeners(); - return; + return true; } final iterable = groupController.items.reversed @@ -329,12 +334,15 @@ class BoardFocusScope extends ChangeNotifier } notifyListeners(); + + return true; } - void clear() { + bool clear() { _deepCopy(); _focusedCards.clear(); notifyListeners(); + return true; } void _focusFirstCard() { diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_shortcut_container.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_shortcut_container.dart index 1035364702..b45b849bf4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_shortcut_container.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_shortcut_container.dart @@ -22,56 +22,7 @@ class BoardShortcutContainer extends StatelessWidget { @override Widget build(BuildContext context) { return AFCallbackShortcuts( - canAcceptEvent: (_, __) => - context.read().isShortcutsEnabled.value, - bindings: { - const SingleActivator(LogicalKeyboardKey.arrowUp): - focusScope.focusPrevious, - const SingleActivator(LogicalKeyboardKey.arrowDown): - focusScope.focusNext, - const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): - focusScope.adjustRangeUp, - const SingleActivator(LogicalKeyboardKey.arrowDown, shift: true): - focusScope.adjustRangeDown, - const SingleActivator(LogicalKeyboardKey.escape): focusScope.clear, - const SingleActivator(LogicalKeyboardKey.keyE): () { - if (focusScope.value.length != 1) { - return; - } - context - .read() - .startEditingRow(focusScope.value.first); - }, - const SingleActivator(LogicalKeyboardKey.keyN): () { - if (focusScope.value.length != 1) { - return; - } - context - .read() - .startCreateBottomRow(focusScope.value.first.groupId); - focusScope.clear(); - }, - const SingleActivator(LogicalKeyboardKey.delete): () => - _removeHandler(context), - const SingleActivator(LogicalKeyboardKey.backspace): () => - _removeHandler(context), - SingleActivator( - LogicalKeyboardKey.arrowUp, - shift: true, - meta: Platform.isMacOS, - control: !Platform.isMacOS, - ): () => _shiftCmdUpHandler(context), - const SingleActivator(LogicalKeyboardKey.enter): () => - _enterHandler(context), - const SingleActivator(LogicalKeyboardKey.numpadEnter): () => - _enterHandler(context), - const SingleActivator(LogicalKeyboardKey.enter, shift: true): () => - _shitEnterHandler(context), - const SingleActivator(LogicalKeyboardKey.comma): () => - _moveGroupToAdjacentGroup(context, true), - const SingleActivator(LogicalKeyboardKey.period): () => - _moveGroupToAdjacentGroup(context, false), - }, + bindings: _shortcutBindings(context), child: FocusScope( child: Focus( child: Builder( @@ -92,16 +43,75 @@ class BoardShortcutContainer extends StatelessWidget { ); } - void _enterHandler(BuildContext context) { + Map _shortcutBindings( + BuildContext context, + ) { + return { + const SingleActivator(LogicalKeyboardKey.arrowUp): + focusScope.focusPrevious, + const SingleActivator(LogicalKeyboardKey.arrowDown): focusScope.focusNext, + const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): + focusScope.adjustRangeUp, + const SingleActivator(LogicalKeyboardKey.arrowDown, shift: true): + focusScope.adjustRangeDown, + const SingleActivator(LogicalKeyboardKey.escape): focusScope.clear, + const SingleActivator(LogicalKeyboardKey.delete): () => + _removeHandler(context), + const SingleActivator(LogicalKeyboardKey.backspace): () => + _removeHandler(context), + SingleActivator( + LogicalKeyboardKey.arrowUp, + shift: true, + meta: Platform.isMacOS, + control: !Platform.isMacOS, + ): () => _shiftCmdUpHandler(context), + const SingleActivator(LogicalKeyboardKey.enter): () => + _enterHandler(context), + const SingleActivator(LogicalKeyboardKey.numpadEnter): () => + _enterHandler(context), + const SingleActivator(LogicalKeyboardKey.enter, shift: true): () => + _shiftEnterHandler(context), + const SingleActivator(LogicalKeyboardKey.comma): () => + _moveGroupToAdjacentGroup(context, true), + const SingleActivator(LogicalKeyboardKey.period): () => + _moveGroupToAdjacentGroup(context, false), + const SingleActivator(LogicalKeyboardKey.keyE): () => + _keyEHandler(context), + const SingleActivator(LogicalKeyboardKey.keyN): () => + _keyNHandler(context), + }; + } + + bool _keyEHandler(BuildContext context) { if (focusScope.value.length != 1) { - return; + return false; + } + context.read().startEditingRow(focusScope.value.first); + return true; + } + + bool _keyNHandler(BuildContext context) { + if (focusScope.value.length != 1) { + return false; + } + context + .read() + .startCreateBottomRow(focusScope.value.first.groupId); + focusScope.clear(); + return true; + } + + bool _enterHandler(BuildContext context) { + if (focusScope.value.length != 1) { + return false; } context .read() .openCardWithRowId(focusScope.value.first.rowId); + return true; } - void _shitEnterHandler(BuildContext context) { + bool _shiftEnterHandler(BuildContext context) { if (focusScope.value.isEmpty) { context .read() @@ -111,10 +121,13 @@ class BoardShortcutContainer extends StatelessWidget { focusScope.value.first, CreateBoardCardRelativePosition.after, ); + } else { + return false; } + return true; } - void _shiftCmdUpHandler(BuildContext context) { + bool _shiftCmdUpHandler(BuildContext context) { if (focusScope.value.isEmpty) { context .read() @@ -124,19 +137,23 @@ class BoardShortcutContainer extends StatelessWidget { focusScope.value.first, CreateBoardCardRelativePosition.before, ); + } else { + return false; } + return true; } - void _removeHandler(BuildContext context) { - if (focusScope.value.isEmpty) { - return; + bool _removeHandler(BuildContext context) { + if (focusScope.value.length != 1) { + return false; } context.read().add(BoardEvent.deleteCards(focusScope.value)); + return true; } - void _moveGroupToAdjacentGroup(BuildContext context, bool toPrevious) { + bool _moveGroupToAdjacentGroup(BuildContext context, bool toPrevious) { if (focusScope.value.length != 1) { - return; + return false; } context.read().add( BoardEvent.moveGroupToAdjacentGroup( @@ -145,5 +162,6 @@ class BoardShortcutContainer extends StatelessWidget { ), ); focusScope.clear(); + return true; } } diff --git a/frontend/appflowy_flutter/lib/plugins/shared/callback_shortcuts.dart b/frontend/appflowy_flutter/lib/plugins/shared/callback_shortcuts.dart index 26e188511e..238b6bd85d 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/callback_shortcuts.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/callback_shortcuts.dart @@ -1,26 +1,24 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -class AFCallbackShortcutsProvider { - final ValueNotifier isShortcutsEnabled = ValueNotifier(true); -} +typedef AFBindingCallback = bool Function(); class AFCallbackShortcuts extends StatelessWidget { const AFCallbackShortcuts({ super.key, required this.bindings, - required this.canAcceptEvent, required this.child, }); - final Map bindings; - final bool Function(FocusNode node, KeyEvent event) canAcceptEvent; + // The bindings for the shortcuts + // + // The result of the callback will be used to determine if the event is handled + final Map bindings; final Widget child; bool _applyKeyEventBinding(ShortcutActivator activator, KeyEvent event) { if (activator.accepts(event, HardwareKeyboard.instance)) { - bindings[activator]!.call(); - return true; + return bindings[activator]?.call() ?? false; } return false; } @@ -31,9 +29,6 @@ class AFCallbackShortcuts extends StatelessWidget { canRequestFocus: false, skipTraversal: true, onKeyEvent: (FocusNode node, KeyEvent event) { - if (!canAcceptEvent(node, event)) { - return KeyEventResult.ignored; - } KeyEventResult result = KeyEventResult.ignored; for (final ShortcutActivator activator in bindings.keys) { result = _applyKeyEventBinding(activator, event) diff --git a/frontend/appflowy_flutter/lib/workspace/application/favorite/favorite_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/favorite/favorite_bloc.dart index 8bc1e549ee..13322807b3 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/favorite/favorite_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/favorite/favorite_bloc.dart @@ -60,7 +60,8 @@ class FavoriteBloc extends Bloc { ); }, toggle: (view) async { - if (view.isFavorite) { + final isFavorited = state.views.any((v) => v.item.id == view.id); + if (isFavorited) { await _service.unpinFavorite(view); } else if (state.pinnedViews.length < 3) { // pin the view if there are less than 3 pinned views diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/favorite_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/favorite_button.dart index 68e8073db3..6e1c377277 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/favorite_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/favorite_button.dart @@ -35,7 +35,7 @@ class ViewFavoriteButton extends StatelessWidget { child: FlowySvg( isFavorite ? FlowySvgs.favorited_s : FlowySvgs.favorite_s, size: const Size.square(18), - blendMode: null, + blendMode: isFavorite ? null : BlendMode.srcIn, ), ), ),