diff --git a/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md b/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md index 76f33711b1..fec474244d 100644 --- a/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md +++ b/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md @@ -6,11 +6,11 @@ * Update example * Add AppFlowy style widget -## 0.0.2 +# 0.0.2 * Update documentation -## 0.0.1 +# 0.0.1 * Support drag and drop column * Support drag and drop column items from one to another 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 01aba725a5..218331d198 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 @@ -26,13 +26,18 @@ class _MultiBoardListExampleState extends State { List a = [ TextItem("Card 1"), TextItem("Card 2"), - // RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'), + RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'), TextItem("Card 4"), + TextItem("Card 5"), + TextItem("Card 6"), + RichTextItem(title: "Card 7", subtitle: 'Aug 1, 2020 4:05 PM'), + RichTextItem(title: "Card 8", subtitle: 'Aug 1, 2020 4:05 PM'), + TextItem("Card 9"), ]; final column1 = AFBoardColumnData(id: "To Do", items: a); final column2 = AFBoardColumnData(id: "In Progress", items: [ - // RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'), - // TextItem("Card 6"), + RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'), + TextItem("Card 11"), ]); final column3 = AFBoardColumnData(id: "Done", items: []); @@ -93,7 +98,7 @@ class _MultiBoardListExampleState extends State { return Align( alignment: Alignment.centerLeft, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40), child: Text(item.s), ), ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart index 20f810a966..d3b795f5f8 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart @@ -6,7 +6,7 @@ const DART_LOG = "Dart_LOG"; class Log { // static const enableLog = bool.hasEnvironment(DART_LOG); // static final shared = Log(); - static const enableLog = false; + static const enableLog = true; static void info(String? message) { if (enableLog) { @@ -16,19 +16,19 @@ class Log { static void debug(String? message) { if (enableLog) { - debugPrint('🐛[Debug]=> $message'); + debugPrint('🐛[Debug] - ${DateTime.now().second}=> $message'); } } static void warn(String? message) { if (enableLog) { - debugPrint('🐛[Warn]=> $message'); + debugPrint('🐛[Warn] - ${DateTime.now().second} => $message'); } } static void trace(String? message) { if (enableLog) { - // debugPrint('❗️[Trace]=> $message'); + debugPrint('❗️[Trace] - ${DateTime.now().second}=> $message'); } } } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart index e8d5471939..d0d102cba3 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart @@ -197,7 +197,8 @@ class AFBoardDataController extends ChangeNotifier assert(index != -1); if (index != -1) { if (index != newIndex) { - // Log.debug('[$BoardPhantomController] update $toColumnId:$index to $toColumnId:$phantomIndex'); + Log.debug( + '[$BoardPhantomController] update $columnId:$index to $columnId:$newIndex'); final item = columnDataController.removeAt(index, notify: false); columnDataController.insert(newIndex, item, notify: false); } 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 f5f7250834..250a4eb020 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 @@ -43,7 +43,7 @@ class FlexDragTargetData extends DragTargetData { } class DraggingState { - final String id; + final String reorderFlexId; /// The member of widget.children currently being dragged. Widget? _draggingWidget; @@ -72,7 +72,7 @@ class DraggingState { /// The additional margin to place around a computed drop area. static const double _dropAreaMargin = 0.0; - DraggingState(this.id); + DraggingState(this.reorderFlexId); Size get dropAreaSize { if (feedbackSize == null) { @@ -132,7 +132,7 @@ class DraggingState { } void updateNextIndex(int index) { - Log.trace('updateNextIndex: $index'); + Log.trace('$reorderFlexId updateNextIndex: $index'); nextIndex = index; } 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 132d3d9bc4..57bd7343d9 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 @@ -1,3 +1,4 @@ +import 'package:appflowy_board/src/utils/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; @@ -222,10 +223,10 @@ class DragTargetAnimation { value: 0, vsync: vsync, duration: reorderAnimationDuration); insertController = AnimationController( - value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 100)); + value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 200)); deleteController = AnimationController( - value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10)); + value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 1)); } void startDragging() { @@ -371,6 +372,7 @@ class _DragTargeMovePlaceholderState extends State { } abstract class FakeDragTargetEventTrigger { + void fakeOnDragStart(void Function(int?) callback); void fakeOnDragEnded(VoidCallback callback); } @@ -421,6 +423,10 @@ class _FakeDragTargetState /// Start insert animation widget.insertAnimationController.forward(from: 0.0); + widget.eventTrigger.fakeOnDragStart((insertIndex) { + Log.debug("[$FakeDragTarget] on drag $insertIndex"); + }); + widget.eventTrigger.fakeOnDragEnded(() { WidgetsBinding.instance.addPostFrameCallback((_) { widget.onDragEnded(widget.eventData.dragTargetData as T); 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 be74b4eef8..1438e396a4 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 @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import '../../utils/log.dart'; @@ -8,6 +10,8 @@ import 'reorder_flex.dart'; /// [DragTargetInterceptor] is used to intercept the [DragTarget]'s /// [onWillAccept], [OnAccept], and [onLeave] event. abstract class DragTargetInterceptor { + String get reorderFlexId; + /// Returns [yes] to receive the [DragTarget]'s event. bool canHandler(FlexDragTargetData dragTargetData); @@ -37,7 +41,7 @@ abstract class OverlapDragTargetDelegate { int dragTargetIndex, ); - bool canMoveTo(String dragTargetId); + int canMoveTo(String dragTargetId); } /// [OverlappingDragTargetInterceptor] is used to receive the overlapping @@ -47,6 +51,7 @@ abstract class OverlapDragTargetDelegate { /// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains /// the passed in dragTarget' reorderFlexId. class OverlappingDragTargetInterceptor extends DragTargetInterceptor { + @override final String reorderFlexId; final List acceptedReorderFlexId; final OverlapDragTargetDelegate delegate; @@ -72,8 +77,11 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor { if (dragTargetId == dragTargetData.reorderFlexId) { delegate.cancel(); } else { - if (delegate.canMoveTo(dragTargetId)) { - delegate.moveTo(dragTargetId, dragTargetData, 0); + final index = delegate.canMoveTo(dragTargetId); + Log.trace( + '[$OverlappingDragTargetInterceptor] move to $dragTargetId at $index'); + if (index != -1) { + delegate.moveTo(dragTargetId, dragTargetData, index); } } @@ -96,6 +104,7 @@ abstract class CrossReorderFlexDragTargetDelegate { } class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor { + @override final String reorderFlexId; final List acceptedReorderFlexIds; final CrossReorderFlexDragTargetDelegate delegate; @@ -119,8 +128,12 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor { /// If the columnId equal to the dragTargetData's columnId, /// it means the dragTarget is dragging on the top of its own list. /// Otherwise, it means the dargTarget was moved to another list. + Log.trace( + "[$CrossReorderFlexDragTargetInterceptor] $reorderFlexId accept ${dragTargetData.reorderFlexId} ${reorderFlexId != dragTargetData.reorderFlexId}"); return reorderFlexId != dragTargetData.reorderFlexId; } else { + Log.trace( + "[$CrossReorderFlexDragTargetInterceptor] not accept ${dragTargetData.reorderFlexId}"); return false; } } @@ -151,6 +164,9 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor { dragTargetIndex, ); + Log.debug( + '[$CrossReorderFlexDragTargetInterceptor] dargTargetIndex: $dragTargetIndex, reorderFlexId: $reorderFlexId'); + if (isNewDragTarget == false) { delegate.updateDragTargetData(reorderFlexId, dragTargetIndex); reorderFlexState.handleOnWillAccept(context, dragTargetIndex); 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 7fa1a405e1..9916523782 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 @@ -36,10 +36,10 @@ class ReorderFlexConfig { final double draggingWidgetOpacity = 0.3; // How long an animation to reorder an element - final Duration reorderAnimationDuration = const Duration(milliseconds: 250); + final Duration reorderAnimationDuration = const Duration(milliseconds: 300); // How long an animation to scroll to an off-screen element - final Duration scrollAnimationDuration = const Duration(milliseconds: 250); + final Duration scrollAnimationDuration = const Duration(milliseconds: 300); final bool useMoveAnimation; @@ -213,8 +213,8 @@ class ReorderFlexState extends State shiftedIndex = dragState.calculateShiftedIndex(childIndex); } - Log.trace( - 'Rebuild: Column:[${dragState.id}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex'); + // Log.trace( + // 'Rebuild: Column:[${dragState.id}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex'); final currentIndex = dragState.currentIndex; final dragPhantomIndex = dragState.phantomIndex; @@ -330,6 +330,8 @@ class ReorderFlexState extends State widget.onDragStarted?.call(draggingIndex); }, onDragEnded: (dragTargetData) { + if (!mounted) return; + Log.debug( "[DragTarget]: Column:[${widget.dataSource.identifier}] end dragging"); _notifier.updateDragTargetIndex(-1); @@ -346,21 +348,21 @@ class ReorderFlexState extends State }); }, onWillAccept: (FlexDragTargetData dragTargetData) { + // Do not receive any events if the Insert item is animating. if (_animation.deleteController.isAnimating) { return false; } assert(widget.dataSource.items.length > dragTargetIndex); - if (_interceptDragTarget( - dragTargetData, - (interceptor) => interceptor.onWillAccept( + if (_interceptDragTarget(dragTargetData, (interceptor) { + interceptor.onWillAccept( context: builderContext, reorderFlexState: this, dragTargetData: dragTargetData, dragTargetId: reorderFlexItem.id, dragTargetIndex: dragTargetIndex, - ), - )) { + ); + })) { return true; } else { return handleOnWillAccept(builderContext, dragTargetIndex); @@ -524,7 +526,7 @@ class ReorderFlexState extends State // screen, then it is already on-screen. final double margin = widget.direction == Axis.horizontal ? dragState.dropAreaSize.width - : dragState.dropAreaSize.height; + : dragState.dropAreaSize.height / 2.0; if (_scrollController.hasClients) { final double scrollOffset = _scrollController.offset; final double topOffset = max( diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart index 0db70d0bae..42f356542b 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart @@ -59,12 +59,13 @@ class BoardPhantomController extends OverlapDragTargetDelegate } void columnStartDragging(String columnId) { - columnsState.setColumnIsDragging(columnId, false); + columnsState.setColumnIsDragging(columnId, true); } /// Remove the phantom in the column when the column is end dragging. void columnEndDragging(String columnId) { - columnsState.setColumnIsDragging(columnId, true); + columnsState.setColumnIsDragging(columnId, false); + if (phantomRecord == null) return; final fromColumnId = phantomRecord!.fromColumnId; @@ -73,19 +74,18 @@ class BoardPhantomController extends OverlapDragTargetDelegate columnsState.notifyDidRemovePhantom(toColumnId); } - if (columnsState.isDragging(fromColumnId) == false) { - return; + if (phantomRecord!.toColumnId == columnId) { + delegate.swapColumnItem( + fromColumnId, + phantomRecord!.fromColumnIndex, + toColumnId, + phantomRecord!.toColumnIndex, + ); + + Log.debug( + "[$BoardPhantomController] did move ${phantomRecord.toString()}"); + phantomRecord = null; } - - delegate.swapColumnItem( - fromColumnId, - phantomRecord!.fromColumnIndex, - toColumnId, - phantomRecord!.toColumnIndex, - ); - - Log.debug("[$BoardPhantomController] did move ${phantomRecord.toString()}"); - phantomRecord = null; } /// Remove the phantom in the column if it contains phantom @@ -113,7 +113,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate PhantomColumnItem(phantomContext), ); - columnsState.notifyDidInsertPhantom(toColumnId); + columnsState.notifyDidInsertPhantom(toColumnId, phantomIndex); } /// Reset or initial the [PhantomRecord] @@ -128,8 +128,9 @@ class BoardPhantomController extends OverlapDragTargetDelegate FlexDragTargetData dragTargetData, int dragTargetIndex, ) { - // Log.debug('[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} ' - // 'to Column:[$columnId]:$index'); + // Log.debug( + // '[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} ' + // 'to Column:[$columnId]:$dragTargetIndex'); phantomRecord = PhantomRecord( toColumnId: columnId, @@ -202,8 +203,23 @@ class BoardPhantomController extends OverlapDragTargetDelegate } @override - bool canMoveTo(String dragTargetId) { - return delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false; + int canMoveTo(String dragTargetId) { + // if (columnsState.isDragging(dragTargetId)) { + // return -1; + // } + + // final controller = delegate.controller(dragTargetId); + // if (controller != null) { + // return controller.columnData.items.length; + // } else { + // return 0; + // } + + if (delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false) { + return 0; + } else { + return -1; + } } } @@ -294,7 +310,7 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger AFColumnItem get itemData => dragTargetData.reorderFlexItem as AFColumnItem; @override - VoidCallback? onInserted; + void Function(int?)? onInserted; @override VoidCallback? onDragEnded; @@ -308,6 +324,11 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger void fakeOnDragEnded(VoidCallback callback) { onDragEnded = callback; } + + @override + void fakeOnDragStart(void Function(int? index) callback) { + onInserted = callback; + } } class PassthroughPhantomWidget extends PhantomWidget { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart index d33b53500d..443d7fb936 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart @@ -14,7 +14,7 @@ class ColumnPhantomStateController { void addColumnListener(String columnId, PassthroughPhantomListener listener) { _stateWithId(columnId).notifier.addListener( - onInserted: (c) => listener.onInserted?.call(), + onInserted: (index) => listener.onInserted?.call(index), onDeleted: () => listener.onDragEnded?.call(), ); } @@ -24,8 +24,8 @@ class ColumnPhantomStateController { _states.remove(columnId); } - void notifyDidInsertPhantom(String columnId) { - _stateWithId(columnId).notifier.insert(); + void notifyDidInsertPhantom(String columnId, int index) { + _stateWithId(columnId).notifier.insert(index); } void notifyDidRemovePhantom(String columnId) { @@ -48,7 +48,7 @@ class ColumnState { } abstract class PassthroughPhantomListener { - VoidCallback? get onInserted; + void Function(int?)? get onInserted; VoidCallback? get onDragEnded; } @@ -57,8 +57,8 @@ class PassthroughPhantomNotifier { final removeNotifier = PhantomDeleteNotifier(); - void insert() { - insertNotifier.insert(); + void insert(int index) { + insertNotifier.insert(index); } void remove() { @@ -66,12 +66,12 @@ class PassthroughPhantomNotifier { } void addListener({ - void Function(PassthroughPhantomContext? insertedPhantom)? onInserted, + void Function(int? insertedIndex)? onInserted, void Function()? onDeleted, }) { if (onInserted != null) { insertNotifier.addListener(() { - onInserted(insertNotifier.insertedPhantom); + onInserted(insertNotifier.insertedIndex); }); } @@ -89,9 +89,11 @@ class PassthroughPhantomNotifier { } class PhantomInsertNotifier extends ChangeNotifier { + int insertedIndex = -1; PassthroughPhantomContext? insertedPhantom; - void insert() { + void insert(int index) { + insertedIndex = index; notifyListeners(); } }