From 5763e289efc16b717e3902544f9d57543ceba74a Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 30 Sep 2022 20:44:41 +0800 Subject: [PATCH] feat: support moving group --- .../board/presentation/board_page.dart | 12 +-- .../appflowy_board/example/lib/main.dart | 2 +- .../example/lib/multi_board_list_example.dart | 7 ++ .../appflowy_board/lib/src/widgets/board.dart | 88 +++++++-------- .../lib/src/widgets/board_group/group.dart | 13 +-- .../src/widgets/board_group/group_data.dart | 20 ++-- .../src/widgets/reorder_flex/drag_state.dart | 6 +- .../src/widgets/reorder_flex/drag_target.dart | 26 +++-- .../reorder_flex/drag_target_interceptor.dart | 5 +- .../widgets/reorder_flex/reorder_flex.dart | 102 +++++++++++++----- .../widgets/reorder_flex/reorder_mixin.dart | 2 +- .../group_entities/group_changeset.rs | 1 + .../src/services/grid_view_editor.rs | 3 +- .../src/services/group/configuration.rs | 1 + 14 files changed, 171 insertions(+), 117 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 69020b5eed..dd5e619b46 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -69,7 +69,6 @@ class BoardContent extends StatefulWidget { class _BoardContentState extends State { late AppFlowyBoardScrollController scrollManager; - final Map cardKeysCache = {}; final config = AppFlowyBoardConfig( groupBackgroundColor: HexColor.fromHex('#F7F8FC'), @@ -139,7 +138,7 @@ class _BoardContentState extends State { .read() .add(BoardEvent.endEditRow(editingRow.row.id)); } else { - scrollManager.scrollToBottom(editingRow.columnId, () { + scrollManager.scrollToBottom(editingRow.columnId, (boardContext) { context .read() .add(BoardEvent.endEditRow(editingRow.row.id)); @@ -247,15 +246,8 @@ class _BoardContentState extends State { ); final groupItemId = columnItem.id + group.id; - ValueKey? key = cardKeysCache[groupItemId]; - if (key == null) { - final newKey = ValueKey(groupItemId); - cardKeysCache[groupItemId] = newKey; - key = newKey; - } - return AppFlowyGroupCard( - key: key, + key: ValueKey(groupItemId), margin: config.cardPadding, decoration: _makeBoxDecoration(context), child: BoardCard( diff --git a/frontend/app_flowy/packages/appflowy_board/example/lib/main.dart b/frontend/app_flowy/packages/appflowy_board/example/lib/main.dart index c881370e03..9e42611e7e 100644 --- a/frontend/app_flowy/packages/appflowy_board/example/lib/main.dart +++ b/frontend/app_flowy/packages/appflowy_board/example/lib/main.dart @@ -34,7 +34,7 @@ class _MyAppState extends State { appBar: AppBar( title: const Text('AppFlowy Board'), ), - body: _examples[_currentIndex], + body: Container(color: Colors.white, child: _examples[_currentIndex]), bottomNavigationBar: BottomNavigationBar( fixedColor: _bottomNavigationColor, showSelectedLabels: true, diff --git a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart index 8bd7ae98e3..2a256a51c4 100644 --- a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart +++ b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart @@ -21,8 +21,11 @@ class _MultiBoardListExampleState extends State { }, ); + late AppFlowyBoardScrollController boardController; + @override void initState() { + boardController = AppFlowyBoardScrollController(); final group1 = AppFlowyGroupData(id: "To Do", name: "To Do", items: [ TextItem("Card 1"), TextItem("Card 2"), @@ -67,12 +70,16 @@ class _MultiBoardListExampleState extends State { child: _buildCard(groupItem), ); }, + boardScrollController: boardController, footerBuilder: (context, columnData) { return AppFlowyGroupFooter( icon: const Icon(Icons.add, size: 20), title: const Text('New'), height: 50, margin: config.groupItemPadding, + onAddButtonClick: () { + boardController.scrollToBottom(columnData.id, (p0) {}); + }, ); }, headerBuilder: (context, columnData) { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart index 4a71595ea6..8aaf94e6d6 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart @@ -13,10 +13,8 @@ import '../rendering/board_overlay.dart'; class AppFlowyBoardScrollController { AppFlowyBoardState? _groupState; - void scrollToBottom(String groupId, VoidCallback? completed) { - _groupState - ?.getReorderFlexState(groupId: groupId) - ?.scrollToBottom(completed); + void scrollToBottom(String groupId, void Function(BuildContext)? completed) { + _groupState?.reorderFlexActionMap[groupId]?.scrollToBottom(completed); } } @@ -133,7 +131,7 @@ class AppFlowyBoard extends StatelessWidget { dataController: controller, scrollController: scrollController, scrollManager: boardScrollController, - columnsState: _groupState, + groupState: _groupState, background: background, delegate: _phantomController, groupConstraints: groupConstraints, @@ -158,7 +156,7 @@ class _AppFlowyBoardContent extends StatefulWidget { final ReorderFlexConfig reorderFlexConfig; final BoxConstraints groupConstraints; final AppFlowyBoardScrollController? scrollManager; - final AppFlowyBoardState columnsState; + final AppFlowyBoardState groupState; final AppFlowyBoardCardBuilder cardBuilder; final AppFlowyBoardHeaderBuilder? headerBuilder; final AppFlowyBoardFooterBuilder? footerBuilder; @@ -171,7 +169,7 @@ class _AppFlowyBoardContent extends StatefulWidget { required this.delegate, required this.dataController, required this.scrollManager, - required this.columnsState, + required this.groupState, this.scrollController, this.background, required this.groupConstraints, @@ -192,8 +190,6 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { GlobalKey(debugLabel: '$_AppFlowyBoardContent overlay key'); late BoardOverlayEntry _overlayEntry; - final Map _reorderFlexKeys = {}; - @override void initState() { _overlayEntry = BoardOverlayEntry( @@ -202,7 +198,7 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { reorderFlexId: widget.dataController.identifier, acceptedReorderFlexId: widget.dataController.groupIds, delegate: widget.delegate, - columnsState: widget.columnsState, + columnsState: widget.groupState, ); final reorderFlex = ReorderFlex( @@ -212,7 +208,7 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { dataSource: widget.dataController, direction: Axis.horizontal, interceptor: interceptor, - reorderable: false, + reorderable: true, children: _buildColumns(), ); @@ -257,18 +253,16 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { dataController: widget.dataController, ); - if (_reorderFlexKeys[columnData.id] == null) { - _reorderFlexKeys[columnData.id] = GlobalObjectKey(columnData.id); - } + final reorderFlexAction = ReorderFlexActionImpl(); + widget.groupState.reorderFlexActionMap[columnData.id] = + reorderFlexAction; - GlobalObjectKey reorderFlexKey = _reorderFlexKeys[columnData.id]!; return ChangeNotifierProvider.value( key: ValueKey(columnData.id), value: widget.dataController.getGroupController(columnData.id), child: Consumer( builder: (context, value, child) { final boardColumn = AppFlowyBoardGroup( - reorderFlexKey: reorderFlexKey, // key: PageStorageKey(columnData.id), margin: _marginFromIndex(columnIndex), itemMargin: widget.config.groupItemPadding, @@ -281,11 +275,11 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { onReorder: widget.dataController.moveGroupItem, cornerRadius: widget.config.cornerRadius, backgroundColor: widget.config.groupBackgroundColor, - dragStateStorage: widget.columnsState, - dragTargetIndexKeyStorage: widget.columnsState, + dragStateStorage: widget.groupState, + dragTargetKeys: widget.groupState, + reorderFlexAction: reorderFlexAction, ); - widget.columnsState.addGroup(columnData.id, boardColumn); return ConstrainedBox( constraints: widget.groupConstraints, child: boardColumn, @@ -356,71 +350,61 @@ class AppFlowyGroupContext { } class AppFlowyBoardState extends DraggingStateStorage - with ReorderDragTargetIndexKeyStorage { + with ReorderDragTargeKeys { + final Map groupDragStates = {}; + final Map> groupDragTargetKeys = {}; + /// Quick access to the [AppFlowyBoardGroup], the [GlobalKey] is bind to the /// AppFlowyBoardGroup's [ReorderFlex] widget. - final Map groupReorderFlexKeys = {}; - final Map groupDragStates = {}; - final Map> groupDragDragTargets = {}; - - void addGroup(String groupId, AppFlowyBoardGroup groupWidget) { - groupReorderFlexKeys[groupId] = groupWidget.reorderFlexKey; - } - - ReorderFlexState? getReorderFlexState({required String groupId}) { - final flexGlobalKey = groupReorderFlexKeys[groupId]; - if (flexGlobalKey == null) return null; - if (flexGlobalKey.currentState is! ReorderFlexState) return null; - final state = flexGlobalKey.currentState as ReorderFlexState; - return state; - } - - ReorderFlex? getReorderFlex({required String groupId}) { - final flexGlobalKey = groupReorderFlexKeys[groupId]; - if (flexGlobalKey == null) return null; - if (flexGlobalKey.currentWidget is! ReorderFlex) return null; - final widget = flexGlobalKey.currentWidget as ReorderFlex; - return widget; - } + final Map reorderFlexActionMap = {}; @override - DraggingState? read(String reorderFlexId) { + DraggingState? readState(String reorderFlexId) { return groupDragStates[reorderFlexId]; } @override - void write(String reorderFlexId, DraggingState state) { + void insertState(String reorderFlexId, DraggingState state) { Log.trace('$reorderFlexId Write dragging state: $state'); groupDragStates[reorderFlexId] = state; } @override - void remove(String reorderFlexId) { + void removeState(String reorderFlexId) { groupDragStates.remove(reorderFlexId); } @override - void addKey( + void insertDragTarget( String reorderFlexId, String key, GlobalObjectKey> value, ) { - Map? group = groupDragDragTargets[reorderFlexId]; + Map? group = groupDragTargetKeys[reorderFlexId]; if (group == null) { group = {}; - groupDragDragTargets[reorderFlexId] = group; + groupDragTargetKeys[reorderFlexId] = group; } group[key] = value; } @override - GlobalObjectKey>? readKey( - String reorderFlexId, String key) { - Map? group = groupDragDragTargets[reorderFlexId]; + GlobalObjectKey>? getDragTarget( + String reorderFlexId, + String key, + ) { + Map? group = groupDragTargetKeys[reorderFlexId]; if (group != null) { return group[key]; } else { return null; } } + + @override + void removeDragTarget(String reorderFlexId) { + groupDragTargetKeys.remove(reorderFlexId); + } } + +class ReorderFlexActionImpl extends ReorderFlexAction {} diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart index 8c27def35d..880a81f666 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart @@ -90,21 +90,21 @@ class AppFlowyBoardGroup extends StatefulWidget { final DraggingStateStorage? dragStateStorage; - final ReorderDragTargetIndexKeyStorage? dragTargetIndexKeyStorage; + final ReorderDragTargeKeys? dragTargetKeys; - final GlobalObjectKey reorderFlexKey; + final ReorderFlexAction? reorderFlexAction; const AppFlowyBoardGroup({ Key? key, - required this.reorderFlexKey, this.headerBuilder, this.footerBuilder, required this.cardBuilder, required this.onReorder, required this.dataSource, required this.phantomController, + this.reorderFlexAction, this.dragStateStorage, - this.dragTargetIndexKeyStorage, + this.dragTargetKeys, this.scrollController, this.onDragStarted, this.onDragEnded, @@ -146,9 +146,9 @@ class _AppFlowyBoardGroupState extends State { ); Widget reorderFlex = ReorderFlex( - key: widget.reorderFlexKey, + key: ValueKey(widget.groupId), dragStateStorage: widget.dragStateStorage, - dragTargetIndexKeyStorage: widget.dragTargetIndexKeyStorage, + dragTargetKeys: widget.dragTargetKeys, scrollController: widget.scrollController, config: widget.config, onDragStarted: (index) { @@ -168,6 +168,7 @@ class _AppFlowyBoardGroupState extends State { }, dataSource: widget.dataSource, interceptor: interceptor, + reorderFlexAction: widget.reorderFlexAction, children: children, ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart index 0dba483715..5223e10e90 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart @@ -41,7 +41,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { void updateGroupName(String newName) { if (groupData.headerData.groupName != newName) { groupData.headerData.groupName = newName; - notifyListeners(); + _notify(); } } @@ -56,7 +56,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { Log.debug('[$AppFlowyGroupController] $groupData remove item at $index'); final item = groupData._items.removeAt(index); if (notify) { - notifyListeners(); + _notify(); } return item; } @@ -81,7 +81,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { '[$AppFlowyGroupController] $groupData move item from $fromIndex to $toIndex'); final item = groupData._items.removeAt(fromIndex); groupData._items.insert(toIndex, item); - notifyListeners(); + _notify(); return true; } @@ -102,7 +102,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { groupData._items.add(item); } - if (notify) notifyListeners(); + if (notify) _notify(); return true; } } @@ -112,7 +112,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { return false; } else { groupData._items.add(item); - if (notify) notifyListeners(); + if (notify) _notify(); return true; } } @@ -135,7 +135,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { '[$AppFlowyGroupController] $groupData replace $removedItem with $newItem at $index'); } - notifyListeners(); + _notify(); } void replaceOrInsertItem(AppFlowyGroupItem newItem) { @@ -143,10 +143,10 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { if (index != -1) { groupData._items.removeAt(index); groupData._items.insert(index, newItem); - notifyListeners(); + _notify(); } else { groupData._items.add(newItem); - notifyListeners(); + _notify(); } } @@ -154,6 +154,10 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { return groupData._items.indexWhere((element) => element.id == item.id) != -1; } + + void _notify() { + notifyListeners(); + } } /// [AppFlowyGroupData] represents the data of each group of the Board. diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart index 9d528bf9f1..8e1a61be1a 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart @@ -69,9 +69,9 @@ class FlexDragTargetData extends DragTargetData { } abstract class DraggingStateStorage { - void write(String reorderFlexId, DraggingState state); - void remove(String reorderFlexId); - DraggingState? read(String reorderFlexId); + void insertState(String reorderFlexId, DraggingState state); + void removeState(String reorderFlexId); + DraggingState? readState(String reorderFlexId); } class DraggingState { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart index 4199e07ec8..fde9c3470a 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart @@ -73,11 +73,15 @@ class ReorderDragTarget extends StatefulWidget { final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder; final AnimationController insertAnimationController; + final AnimationController deleteAnimationController; final bool useMoveAnimation; + final bool draggable; + final double draggingOpacity; + const ReorderDragTarget({ Key? key, required this.child, @@ -94,6 +98,7 @@ class ReorderDragTarget extends StatefulWidget { this.onAccept, this.onLeave, this.draggableTargetBuilder, + this.draggingOpacity = 0.3, }) : super(key: key); @override @@ -164,6 +169,7 @@ class _ReorderDragTargetState feedback: feedbackBuilder, childWhenDragging: IgnorePointerWidget( useIntrinsicSize: !widget.useMoveAnimation, + opacity: widget.draggingOpacity, child: widget.child, ), onDragStarted: () { @@ -195,7 +201,10 @@ class _ReorderDragTargetState } Widget _buildDraggableFeedback( - BuildContext context, BoxConstraints constraints, Widget child) { + BuildContext context, + BoxConstraints constraints, + Widget child, + ) { return Transform( transform: Matrix4.rotationZ(0), alignment: FractionalOffset.topLeft, @@ -205,7 +214,7 @@ class _ReorderDragTargetState clipBehavior: Clip.hardEdge, child: ConstrainedBox( constraints: constraints, - child: Opacity(opacity: 0.3, child: child), + child: Opacity(opacity: widget.draggingOpacity, child: child), ), ), ); @@ -274,8 +283,11 @@ class DragTargetAnimation { class IgnorePointerWidget extends StatelessWidget { final Widget? child; final bool useIntrinsicSize; + final double opacity; + const IgnorePointerWidget({ required this.child, + required this.opacity, this.useIntrinsicSize = false, Key? key, }) : super(key: key); @@ -286,11 +298,10 @@ class IgnorePointerWidget extends StatelessWidget { ? child : SizedBox(width: 0.0, height: 0.0, child: child); - final opacity = useIntrinsicSize ? 0.3 : 0.0; return IgnorePointer( ignoring: true, child: Opacity( - opacity: opacity, + opacity: useIntrinsicSize ? opacity : 0.0, child: sizedChild, ), ); @@ -300,8 +311,10 @@ class IgnorePointerWidget extends StatelessWidget { class AbsorbPointerWidget extends StatelessWidget { final Widget? child; final bool useIntrinsicSize; + final double opacity; const AbsorbPointerWidget({ required this.child, + required this.opacity, this.useIntrinsicSize = false, Key? key, }) : super(key: key); @@ -312,10 +325,9 @@ class AbsorbPointerWidget extends StatelessWidget { ? child : SizedBox(width: 0.0, height: 0.0, child: child); - final opacity = useIntrinsicSize ? 0.3 : 0.0; return AbsorbPointer( child: Opacity( - opacity: opacity, + opacity: useIntrinsicSize ? opacity : 0.0, child: sizedChild, ), ); @@ -494,6 +506,7 @@ class _FakeDragTargetState sizeFactor: widget.deleteAnimationController, axis: Axis.vertical, child: AbsorbPointerWidget( + opacity: 0.3, child: widget.child, ), ); @@ -503,6 +516,7 @@ class _FakeDragTargetState axis: Axis.vertical, child: AbsorbPointerWidget( useIntrinsicSize: true, + opacity: 0.3, child: widget.child, ), ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart index ecf91d6934..21a67a3c7d 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart @@ -81,7 +81,7 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor { delegate.cancel(); } else { // Ignore the event if the dragTarget overlaps with the other column's dragTargets. - final columnKeys = columnsState.groupDragDragTargets[dragTargetId]; + final columnKeys = columnsState.groupDragTargetKeys[dragTargetId]; if (columnKeys != null) { final keys = columnKeys.values.toList(); if (dragTargetData.isOverlapWithWidgets(keys)) { @@ -102,8 +102,7 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor { delegate.dragTargetDidMoveToReorderFlex( dragTargetId, dragTargetData, index); - columnsState - .getReorderFlexState(groupId: dragTargetId) + columnsState.reorderFlexActionMap[dragTargetId] ?.resetDragTargetIndex(index); } }); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart index 1317ad9ab8..f96136c247 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart @@ -31,9 +31,32 @@ abstract class ReoderFlexItem { String get id; } -abstract class ReorderDragTargetIndexKeyStorage { - void addKey(String reorderFlexId, String key, GlobalObjectKey value); - GlobalObjectKey? readKey(String reorderFlexId, String key); +/// Cache each dragTarget's key. +/// For the moment, the key is used to locate the render object that will +/// be passed in the [ScrollPosition]'s [ensureVisible] function. +/// +abstract class ReorderDragTargeKeys { + void insertDragTarget( + String reorderFlexId, + String key, + GlobalObjectKey value, + ); + + GlobalObjectKey? getDragTarget( + String reorderFlexId, + String key, + ); + + void removeDragTarget(String reorderFlexId); +} + +abstract class ReorderFlexAction { + void Function(void Function(BuildContext)?)? _scrollToBottom; + void Function(void Function(BuildContext)?) get scrollToBottom => + _scrollToBottom!; + + void Function(int)? _resetDragTargetIndex; + void Function(int) get resetDragTargetIndex => _resetDragTargetIndex!; } class ReorderFlexConfig { @@ -78,9 +101,12 @@ class ReorderFlex extends StatefulWidget { final DragTargetInterceptor? interceptor; + /// Save the [DraggingState] if the current [ReorderFlex] get reinitialize. final DraggingStateStorage? dragStateStorage; - final ReorderDragTargetIndexKeyStorage? dragTargetIndexKeyStorage; + final ReorderDragTargeKeys? dragTargetKeys; + + final ReorderFlexAction? reorderFlexAction; final bool reorderable; @@ -93,10 +119,11 @@ class ReorderFlex extends StatefulWidget { required this.onReorder, this.reorderable = true, this.dragStateStorage, - this.dragTargetIndexKeyStorage, + this.dragTargetKeys, this.onDragStarted, this.onDragEnded, this.interceptor, + this.reorderFlexAction, this.direction = Axis.vertical, }) : assert(children.every((Widget w) => w.key != null), 'All child must have a key.'), @@ -109,7 +136,7 @@ class ReorderFlex extends StatefulWidget { } class ReorderFlexState extends State - with ReorderFlexMinxi, TickerProviderStateMixin { + with ReorderFlexMixin, TickerProviderStateMixin { /// Controls scrolls and measures scroll progress. late ScrollController _scrollController; @@ -131,11 +158,11 @@ class ReorderFlexState extends State void initState() { _notifier = ReorderFlexNotifier(); final flexId = widget.reorderFlexId; - dragState = widget.dragStateStorage?.read(flexId) ?? + dragState = widget.dragStateStorage?.readState(flexId) ?? DraggingState(widget.reorderFlexId); Log.trace('[DragTarget] init dragState: $dragState'); - widget.dragStateStorage?.remove(flexId); + widget.dragStateStorage?.removeState(flexId); _animation = DragTargetAnimation( reorderAnimationDuration: widget.config.reorderAnimationDuration, @@ -148,6 +175,14 @@ class ReorderFlexState extends State vsync: this, ); + widget.reorderFlexAction?._scrollToBottom = (fn) { + scrollToBottom(fn); + }; + + widget.reorderFlexAction?._resetDragTargetIndex = (index) { + resetDragTargetIndex(index); + }; + super.initState(); } @@ -184,7 +219,7 @@ class ReorderFlexState extends State final indexKey = GlobalObjectKey(child.key!); // Save the index key for quick access - widget.dragTargetIndexKeyStorage?.addKey( + widget.dragTargetKeys?.insertDragTarget( widget.reorderFlexId, item.id, indexKey, @@ -212,6 +247,7 @@ class ReorderFlexState extends State } _animation.dispose(); + widget.dragTargetKeys?.removeDragTarget(widget.reorderFlexId); super.dispose(); } @@ -236,8 +272,12 @@ class ReorderFlexState extends State /// [childIndex]: the index of the child in a list Widget _wrap(Widget child, int childIndex, GlobalObjectKey indexKey) { return Builder(builder: (context) { - final ReorderDragTarget dragTarget = - _buildDragTarget(context, child, childIndex, indexKey); + final ReorderDragTarget dragTarget = _buildDragTarget( + context, + child, + childIndex, + indexKey, + ); int shiftedIndex = childIndex; if (dragState.isOverlapWithPhantom()) { @@ -342,6 +382,15 @@ class ReorderFlexState extends State }); } + static ReorderFlexState of(BuildContext context) { + if (context is StatefulElement && context.state is ReorderFlexState) { + return context.state as ReorderFlexState; + } + final ReorderFlexState? result = + context.findAncestorStateOfType(); + return result!; + } + ReorderDragTarget _buildDragTarget( BuildContext builderContext, Widget child, @@ -364,7 +413,7 @@ class ReorderFlexState extends State "[DragTarget] Group:[${widget.dataSource.identifier}] start dragging item at $draggingIndex"); _startDragging(draggingWidget, draggingIndex, size); widget.onDragStarted?.call(draggingIndex); - widget.dragStateStorage?.remove(widget.reorderFlexId); + widget.dragStateStorage?.removeState(widget.reorderFlexId); }, onDragMoved: (dragTargetData, offset) { dragTargetData.dragTargetOffset = offset; @@ -435,6 +484,7 @@ class ReorderFlexState extends State draggableTargetBuilder: widget.interceptor?.draggableTargetBuilder, useMoveAnimation: widget.config.useMoveAnimation, draggable: widget.reorderable, + draggingOpacity: widget.config.draggingWidgetOpacity, child: child, ); } @@ -487,7 +537,7 @@ class ReorderFlexState extends State } dragState.setStartDraggingIndex(dragTargetIndex); - widget.dragStateStorage?.write( + widget.dragStateStorage?.insertState( widget.reorderFlexId, dragState, ); @@ -581,46 +631,46 @@ class ReorderFlexState extends State } } - void scrollToBottom(VoidCallback? completed) { + void scrollToBottom(void Function(BuildContext)? completed) { if (_scrolling) { - completed?.call(); + completed?.call(context); return; } if (widget.dataSource.items.isNotEmpty) { final item = widget.dataSource.items.last; - final indexKey = widget.dragTargetIndexKeyStorage?.readKey( + final dragTargetKey = widget.dragTargetKeys?.getDragTarget( widget.reorderFlexId, item.id, ); - if (indexKey == null) { - completed?.call(); + if (dragTargetKey == null) { + completed?.call(context); return; } - final indexContext = indexKey.currentContext; - if (indexContext == null || _scrollController.hasClients == false) { - completed?.call(); + final dragTargetContext = dragTargetKey.currentContext; + if (dragTargetContext == null || _scrollController.hasClients == false) { + completed?.call(context); return; } - final renderObject = indexContext.findRenderObject(); - if (renderObject != null) { + final dragTargetRenderObject = dragTargetContext.findRenderObject(); + if (dragTargetRenderObject != null) { _scrolling = true; _scrollController.position .ensureVisible( - renderObject, + dragTargetRenderObject, alignment: 0.5, duration: const Duration(milliseconds: 120), ) .then((value) { setState(() { _scrolling = false; - completed?.call(); + completed?.call(context); }); }); } else { - completed?.call(); + completed?.call(context); } } } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_mixin.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_mixin.dart index accdaa866b..742d5dd7a6 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_mixin.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_mixin.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; import '../transitions.dart'; import 'drag_target.dart'; -mixin ReorderFlexMinxi { +mixin ReorderFlexMixin { @protected Widget makeAppearingWidget( Widget child, diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs index 63b883d570..b26192a3b4 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs @@ -98,6 +98,7 @@ pub struct MoveGroupPayloadPB { pub to_group_id: String, } +#[derive(debug, Debug)] pub struct MoveGroupParams { pub view_id: String, pub from_group_id: String, diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index c7736c6fdc..eac17188c1 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -173,6 +173,7 @@ impl GridViewRevisionEditor { Ok(groups.into_iter().map(GroupPB::from).collect()) } + #[tracing::instrument(level = "trace", err)] pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { let _ = self .group_controller @@ -180,7 +181,7 @@ impl GridViewRevisionEditor { .await .move_group(¶ms.from_group_id, ¶ms.to_group_id)?; match self.group_controller.read().await.get_group(¶ms.from_group_id) { - None => {} + None => tracing::warn!("Can not find the group with id: {}", params.from_group_id), Some((index, group)) => { let inserted_group = InsertedGroupPB { group: GroupPB::from(group), diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs index 07e5ba45d4..da676598ca 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs @@ -135,6 +135,7 @@ where self.mut_configuration(|configuration| { let from_index = configuration.groups.iter().position(|group| group.id == from_id); let to_index = configuration.groups.iter().position(|group| group.id == to_id); + tracing::trace!("Swap group index:{:?} with index:{:?}", from_index, to_index); if let (Some(from), Some(to)) = (from_index, to_index) { configuration.groups.swap(from, to); }