diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index a4a892a7fc..8afdb15051 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -14,12 +14,14 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:collection'; import 'board_data_controller.dart'; +import 'group_controller.dart'; part 'board_bloc.freezed.dart'; class BoardBloc extends Bloc { final BoardDataController _dataController; - late final AFBoardDataController boardDataController; + late final AFBoardDataController afBoardDataController; + List groupControllers = []; GridFieldCache get fieldCache => _dataController.fieldCache; String get gridId => _dataController.gridId; @@ -27,7 +29,7 @@ class BoardBloc extends Bloc { BoardBloc({required ViewPB view}) : _dataController = BoardDataController(view: view), super(BoardState.initial(view.id)) { - boardDataController = AFBoardDataController( + afBoardDataController = AFBoardDataController( onMoveColumn: ( fromIndex, toIndex, @@ -71,9 +73,6 @@ class BoardBloc extends Bloc { didReceiveGridUpdate: (GridPB grid) { emit(state.copyWith(grid: Some(grid))); }, - didReceiveGroups: (List groups) { - emit(state.copyWith(groups: groups)); - }, didReceiveRows: (List rowInfos) { emit(state.copyWith(rowInfos: rowInfos)); }, @@ -85,9 +84,24 @@ class BoardBloc extends Bloc { @override Future close() async { await _dataController.dispose(); + for (final controller in groupControllers) { + controller.dispose(); + } return super.close(); } + void initializeGroups(List groups) { + for (final group in groups) { + final delegate = GroupControllerDelegateImpl(afBoardDataController); + final controller = GroupController( + group: group, + delegate: delegate, + ); + controller.startListening(); + groupControllers.add(controller); + } + } + GridRowCache? getRowCache(String blockId) { final GridBlockCache? blockCache = _dataController.blocks[blockId]; return blockCache?.rowCache; @@ -100,7 +114,7 @@ class BoardBloc extends Bloc { add(BoardEvent.didReceiveGridUpdate(grid)); } }, - onGroupChanged: (groups) { + didLoadGroups: (groups) { List columns = groups.map((group) { return AFBoardColumnData( id: group.groupId, @@ -110,7 +124,8 @@ class BoardBloc extends Bloc { ); }).toList(); - boardDataController.addColumns(columns); + afBoardDataController.addColumns(columns); + initializeGroups(groups); }, onRowsChanged: (List rowInfos, RowsChangedReason reason) { add(BoardEvent.didReceiveRows(rowInfos)); @@ -155,8 +170,6 @@ class BoardEvent with _$BoardEvent { const factory BoardEvent.initial() = InitialGrid; const factory BoardEvent.createRow(String groupId) = _CreateRow; const factory BoardEvent.endEditRow(String rowId) = _EndEditRow; - const factory BoardEvent.didReceiveGroups(List groups) = - _DidReceiveGroup; const factory BoardEvent.didReceiveRows(List rowInfos) = _DidReceiveRows; const factory BoardEvent.didReceiveGridUpdate( @@ -169,7 +182,6 @@ class BoardState with _$BoardState { const factory BoardState({ required String gridId, required Option grid, - required List groups, required Option editingRow, required List rowInfos, required GridLoadingState loadingState, @@ -177,7 +189,6 @@ class BoardState with _$BoardState { factory BoardState.initial(String gridId) => BoardState( rowInfos: [], - groups: [], grid: none(), gridId: gridId, editingRow: none(), @@ -228,3 +239,27 @@ class CreateCardItem extends AFColumnItem { @override String get id => '$CreateCardItem'; } + +class GroupControllerDelegateImpl extends GroupControllerDelegate { + final AFBoardDataController controller; + + GroupControllerDelegateImpl(this.controller); + + @override + void insertRow(String groupId, RowPB row, int? index) { + final item = BoardColumnItem(row: row); + if (index != null) { + controller.insertColumnItem(groupId, index, item); + } else { + controller.addColumnItem(groupId, item); + } + } + + @override + void removeRow(String groupId, String rowId) { + controller.removeColumnItem(groupId, rowId); + } + + @override + void updateRow(String groupId, RowPB row) {} +} diff --git a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart index e4b4f90520..1d17431713 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart @@ -12,7 +12,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; typedef OnFieldsChanged = void Function(UnmodifiableListView); typedef OnGridChanged = void Function(GridPB); -typedef OnGroupChanged = void Function(List); +typedef DidLoadGroups = void Function(List); typedef OnRowsChanged = void Function( List, RowsChangedReason, @@ -30,7 +30,7 @@ class BoardDataController { OnFieldsChanged? _onFieldsChanged; OnGridChanged? _onGridChanged; - OnGroupChanged? _onGroupChanged; + DidLoadGroups? _didLoadGroup; OnRowsChanged? _onRowsChanged; OnError? _onError; @@ -51,13 +51,13 @@ class BoardDataController { void addListener({ OnGridChanged? onGridChanged, OnFieldsChanged? onFieldsChanged, - OnGroupChanged? onGroupChanged, + DidLoadGroups? didLoadGroups, OnRowsChanged? onRowsChanged, OnError? onError, }) { _onGridChanged = onGridChanged; _onFieldsChanged = onFieldsChanged; - _onGroupChanged = onGroupChanged; + _didLoadGroup = didLoadGroups; _onRowsChanged = onRowsChanged; _onError = onError; @@ -133,7 +133,7 @@ class BoardDataController { return Future( () => result.fold( (groups) { - _onGroupChanged?.call(groups.items); + _didLoadGroup?.call(groups.items); }, (err) => _onError?.call(err), ), diff --git a/frontend/app_flowy/lib/plugins/board/application/group_controller.dart b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart new file mode 100644 index 0000000000..a0cd5c3ced --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart @@ -0,0 +1,49 @@ +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; + +import 'group_listener.dart'; + +abstract class GroupControllerDelegate { + void removeRow(String groupId, String rowId); + void insertRow(String groupId, RowPB row, int? index); + void updateRow(String groupId, RowPB row); +} + +class GroupController { + final GroupPB group; + final GroupListener _listener; + final GroupControllerDelegate delegate; + + GroupController({required this.group, required this.delegate}) + : _listener = GroupListener(group); + + void startListening() { + _listener.start(onGroupChanged: (result) { + result.fold( + (GroupRowsChangesetPB changeset) { + for (final insertedRow in changeset.insertedRows) { + final index = insertedRow.hasIndex() ? insertedRow.index : null; + delegate.insertRow( + group.groupId, + insertedRow.row, + index, + ); + } + + for (final deletedRow in changeset.deletedRows) { + delegate.removeRow(group.groupId, deletedRow); + } + + for (final updatedRow in changeset.updatedRows) { + delegate.updateRow(group.groupId, updatedRow); + } + }, + (err) => Log.error(err), + ); + }); + } + + Future dispose() async { + _listener.stop(); + } +} diff --git a/frontend/app_flowy/lib/plugins/board/application/group_listener.dart b/frontend/app_flowy/lib/plugins/board/application/group_listener.dart new file mode 100644 index 0000000000..797177deca --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/application/group_listener.dart @@ -0,0 +1,51 @@ +import 'dart:typed_data'; + +import 'package:app_flowy/core/grid_notification.dart'; +import 'package:flowy_infra/notifier.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart'; + +typedef UpdateGroupNotifiedValue = Either; + +class GroupListener { + final GroupPB group; + PublishNotifier? _groupNotifier = PublishNotifier(); + GridNotificationListener? _listener; + GroupListener(this.group); + + void start({ + required void Function(UpdateGroupNotifiedValue) onGroupChanged, + }) { + _groupNotifier?.addPublishListener(onGroupChanged); + _listener = GridNotificationListener( + objectId: group.groupId, + handler: _handler, + ); + } + + void _handler( + GridNotification ty, + Either result, + ) { + switch (ty) { + case GridNotification.DidUpdateGroup: + result.fold( + (payload) => _groupNotifier?.value = + left(GroupRowsChangesetPB.fromBuffer(payload)), + (error) => _groupNotifier?.value = right(error), + ); + break; + default: + break; + } + } + + Future stop() async { + await _listener?.stop(); + _groupNotifier?.dispose(); + _groupNotifier = null; + } +} 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 faf7e193b0..1fb31abcc7 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -55,7 +55,7 @@ class BoardContent extends StatelessWidget { child: AFBoard( // key: UniqueKey(), scrollController: ScrollController(), - dataController: context.read().boardDataController, + dataController: context.read().afBoardDataController, headerBuilder: _buildHeader, footBuilder: _buildFooter, cardBuilder: (_, data) => _buildCard(context, data), diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart index 12e0f90b01..816f32fe16 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart @@ -18,14 +18,13 @@ class FieldService { FieldService({required this.gridId, required this.fieldId}); Future> moveField(int fromIndex, int toIndex) { - final payload = MoveItemPayloadPB.create() + final payload = MoveFieldPayloadPB.create() ..gridId = gridId - ..itemId = fieldId - ..ty = MoveItemTypePB.MoveField + ..fieldId = fieldId ..fromIndex = fromIndex ..toIndex = toIndex; - return GridEventMoveItem(payload).send(); + return GridEventMoveField(payload).send(); } Future> updateField({ diff --git a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart index 4315fff38b..8c46ce18b2 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart @@ -23,9 +23,9 @@ class GridFFIService { } Future> createRow({Option? startRowId}) { - CreateRowPayloadPB payload = CreateRowPayloadPB.create()..gridId = gridId; + var payload = CreateTableRowPayloadPB.create()..gridId = gridId; startRowId?.fold(() => null, (id) => payload.startRowId = id); - return GridEventCreateRow(payload).send(); + return GridEventCreateTableRow(payload).send(); } Future> createBoardCard(String groupId) { diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart index 0f056a4006..e610734064 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart @@ -4,6 +4,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart'; class RowFFIService { final String gridId; @@ -14,23 +15,32 @@ class RowFFIService { {required this.gridId, required this.blockId, required this.rowId}); Future> createRow() { - CreateRowPayloadPB payload = CreateRowPayloadPB.create() + final payload = CreateTableRowPayloadPB.create() ..gridId = gridId ..startRowId = rowId; - return GridEventCreateRow(payload).send(); + return GridEventCreateTableRow(payload).send(); } - Future> moveRow( - String rowId, int fromIndex, int toIndex) { - final payload = MoveItemPayloadPB.create() - ..gridId = gridId - ..itemId = rowId - ..ty = MoveItemTypePB.MoveRow + Future> moveRow({ + required String rowId, + required int fromIndex, + required int toIndex, + required GridLayout layout, + String? upperRowId, + }) { + var payload = MoveRowPayloadPB.create() + ..viewId = gridId + ..rowId = rowId + ..layout = layout ..fromIndex = fromIndex ..toIndex = toIndex; - return GridEventMoveItem(payload).send(); + if (upperRowId != null) { + payload.upperRowId = upperRowId; + } + + return GridEventMoveRow(payload).send(); } Future> getRow() { 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 f4ce09fc2c..20824ba6b9 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 @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'board_column/board_column.dart'; import 'board_column/board_column_data.dart'; import 'board_data.dart'; -import 'reorder_flex/drag_target_inteceptor.dart'; +import 'reorder_flex/drag_target_interceptor.dart'; import 'reorder_flex/reorder_flex.dart'; import 'reorder_phantom/phantom_controller.dart'; import '../rendering/board_overlay.dart'; @@ -143,7 +143,7 @@ class _BoardContentState extends State { void initState() { _overlayEntry = BoardOverlayEntry( builder: (BuildContext context) { - final interceptor = OverlappingDragTargetInteceptor( + final interceptor = OverlappingDragTargetInterceptor( reorderFlexId: widget.dataController.identifier, acceptedReorderFlexId: widget.dataController.columnIds, delegate: widget.delegate, diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart index 3873bc222d..cbc537810e 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart @@ -5,7 +5,7 @@ import '../../rendering/board_overlay.dart'; import '../../utils/log.dart'; import '../reorder_phantom/phantom_controller.dart'; import '../reorder_flex/reorder_flex.dart'; -import '../reorder_flex/drag_target_inteceptor.dart'; +import '../reorder_flex/drag_target_interceptor.dart'; import 'board_column_data.dart'; typedef OnColumnDragStarted = void Function(int index); @@ -37,7 +37,7 @@ typedef AFBoardColumnFooterBuilder = Widget Function( AFBoardColumnData columnData, ); -abstract class AFBoardColumnDataDataSource extends ReoderFlextDataSource { +abstract class AFBoardColumnDataDataSource extends ReoderFlexDataSource { AFBoardColumnData get columnData; List get acceptedColumnIds; 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 6e3f45fc7d..6208dbd0f0 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 @@ -24,7 +24,7 @@ typedef OnMoveColumnItemToColumn = void Function( ); class AFBoardDataController extends ChangeNotifier - with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlextDataSource { + with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlexDataSource { final List _columnDatas = []; final OnMoveColumn? onMoveColumn; final OnMoveColumnItem? onMoveColumnItem; 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 a6a09a9770..132d3d9bc4 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 @@ -13,14 +13,14 @@ abstract class ReorderFlexDraggableTargetBuilder { Widget child, DragTargetOnStarted onDragStarted, DragTargetOnEnded onDragEnded, - DragTargetWillAccpet onWillAccept, + DragTargetWillAccepted onWillAccept, AnimationController insertAnimationController, AnimationController deleteAnimationController, ); } /// -typedef DragTargetWillAccpet = bool Function( +typedef DragTargetWillAccepted = bool Function( T dragTargetData); /// @@ -51,7 +51,7 @@ class ReorderDragTarget extends StatefulWidget { /// /// [toAccept] represents the dragTarget index, which is the value passed in /// when creating the [ReorderDragTarget]. - final DragTargetWillAccpet onWillAccept; + final DragTargetWillAccepted onWillAccept; /// Called when an acceptable piece of data was dropped over this drag target. /// @@ -228,7 +228,7 @@ class DragTargetAnimation { value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10)); } - void startDargging() { + void startDragging() { entranceController.value = 1.0; } @@ -386,7 +386,7 @@ class FakeDragTarget extends StatefulWidget { final FakeDragTargetEventData eventData; final DragTargetOnStarted onDragStarted; final DragTargetOnEnded onDragEnded; - final DragTargetWillAccpet onWillAccept; + final DragTargetWillAccepted onWillAccept; final Widget child; final AnimationController insertAnimationController; final AnimationController deleteAnimationController; diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_inteceptor.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart similarity index 96% rename from frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_inteceptor.dart rename to frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart index da529819dd..be74b4eef8 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_inteceptor.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart @@ -40,18 +40,18 @@ abstract class OverlapDragTargetDelegate { bool canMoveTo(String dragTargetId); } -/// [OverlappingDragTargetInteceptor] is used to receive the overlapping +/// [OverlappingDragTargetInterceptor] is used to receive the overlapping /// [DragTarget] event. If a [DragTarget] child is [DragTarget], it will /// receive the [DragTarget] event when being dragged. /// /// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains /// the passed in dragTarget' reorderFlexId. -class OverlappingDragTargetInteceptor extends DragTargetInterceptor { +class OverlappingDragTargetInterceptor extends DragTargetInterceptor { final String reorderFlexId; final List acceptedReorderFlexId; final OverlapDragTargetDelegate delegate; - OverlappingDragTargetInteceptor({ + OverlappingDragTargetInterceptor({ required this.delegate, required this.reorderFlexId, required this.acceptedReorderFlexId, 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 27af28d778..7fa1a405e1 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 @@ -7,25 +7,25 @@ import '../../utils/log.dart'; import 'reorder_mixin.dart'; import 'drag_target.dart'; import 'drag_state.dart'; -import 'drag_target_inteceptor.dart'; +import 'drag_target_interceptor.dart'; typedef OnDragStarted = void Function(int index); typedef OnDragEnded = void Function(); typedef OnReorder = void Function(int fromIndex, int toIndex); typedef OnDeleted = void Function(int deletedIndex); typedef OnInserted = void Function(int insertedIndex); -typedef OnReveivePassedInPhantom = void Function( +typedef OnReceivePassedInPhantom = void Function( FlexDragTargetData dragTargetData, int phantomIndex); -abstract class ReoderFlextDataSource { +abstract class ReoderFlexDataSource { /// [identifier] represents the id the [ReorderFlex]. It must be unique. String get identifier; - /// The number of [ReoderFlexItem]s will be displaied in the [ReorderFlex]. + /// The number of [ReoderFlexItem]s will be displayed in the [ReorderFlex]. UnmodifiableListView get items; } -/// Each item displaied in the [ReorderFlex] required to implement the [ReoderFlexItem]. +/// Each item displayed in the [ReorderFlex] required to implement the [ReoderFlexItem]. abstract class ReoderFlexItem { /// [id] is used to identify the item. It must be unique. String get id; @@ -70,7 +70,7 @@ class ReorderFlex extends StatefulWidget { /// [onDragEnded] is called when dragTarget did end dragging final OnDragEnded? onDragEnded; - final ReoderFlextDataSource dataSource; + final ReoderFlexDataSource dataSource; final DragTargetInterceptor? interceptor; @@ -187,7 +187,7 @@ class ReorderFlexState extends State void _requestAnimationToNextIndex({bool isAcceptingNewTarget = false}) { /// Update the dragState and animate to the next index if the current /// dragging animation is completed. Otherwise, it will get called again - /// when the animation finishs. + /// when the animation finish. if (_animation.entranceController.isCompleted) { dragState.removePhantom(); @@ -425,7 +425,7 @@ class ReorderFlexState extends State ) { setState(() { dragState.startDragging(draggingWidget, dragIndex, feedbackSize); - _animation.startDargging(); + _animation.startDragging(); }); } 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 1ab7b2da23..0db70d0bae 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 @@ -1,9 +1,10 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + import '../../utils/log.dart'; import '../board_column/board_column_data.dart'; import '../reorder_flex/drag_state.dart'; import '../reorder_flex/drag_target.dart'; -import '../reorder_flex/drag_target_inteceptor.dart'; +import '../reorder_flex/drag_target_interceptor.dart'; import 'phantom_state.dart'; abstract class BoardPhantomControllerDelegate { @@ -61,7 +62,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate columnsState.setColumnIsDragging(columnId, false); } - /// Remove the phanton in the column when the column is end dragging. + /// Remove the phantom in the column when the column is end dragging. void columnEndDragging(String columnId) { columnsState.setColumnIsDragging(columnId, true); if (phantomRecord == null) return; @@ -331,7 +332,7 @@ class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder { Widget child, DragTargetOnStarted onDragStarted, DragTargetOnEnded onDragEnded, - DragTargetWillAccpet onWillAccept, + DragTargetWillAccepted onWillAccept, AnimationController insertAnimationController, AnimationController deleteAnimationController, ) { diff --git a/frontend/rust-lib/flowy-grid/src/dart_notification.rs b/frontend/rust-lib/flowy-grid/src/dart_notification.rs index 4108da1f11..0bba5bbc11 100644 --- a/frontend/rust-lib/flowy-grid/src/dart_notification.rs +++ b/frontend/rust-lib/flowy-grid/src/dart_notification.rs @@ -11,7 +11,7 @@ pub enum GridNotification { DidUpdateRow = 30, DidUpdateCell = 40, DidUpdateField = 50, - DidUpdateBoard = 60, + DidUpdateGroup = 60, } impl std::default::Default for GridNotification { diff --git a/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs index 6657e0e05a..f4200d81cf 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs @@ -1,5 +1,5 @@ -use crate::entities::{BlockPB, FieldIdPB}; -use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; +use crate::entities::{BlockPB, FieldIdPB, GridLayout}; +use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; @@ -52,25 +52,51 @@ impl std::convert::From<&str> for GridBlockIdPB { } } -#[derive(Debug, Clone, ProtoBuf_Enum)] -pub enum MoveItemTypePB { - MoveField = 0, - MoveRow = 1, -} - -impl std::default::Default for MoveItemTypePB { - fn default() -> Self { - MoveItemTypePB::MoveField - } -} - #[derive(Debug, Clone, Default, ProtoBuf)] -pub struct MoveItemPayloadPB { +pub struct MoveFieldPayloadPB { #[pb(index = 1)] pub grid_id: String, #[pb(index = 2)] - pub item_id: String, + pub field_id: String, + + #[pb(index = 3)] + pub from_index: i32, + + #[pb(index = 4)] + pub to_index: i32, +} + +#[derive(Clone)] +pub struct MoveFieldParams { + pub grid_id: String, + pub field_id: String, + pub from_index: i32, + pub to_index: i32, +} + +impl TryInto for MoveFieldPayloadPB { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; + let item_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::InvalidData)?; + Ok(MoveFieldParams { + grid_id: grid_id.0, + field_id: item_id.0, + from_index: self.from_index, + to_index: self.to_index, + }) + } +} + +#[derive(Debug, Clone, Default, ProtoBuf)] +pub struct MoveRowPayloadPB { + #[pb(index = 1)] + pub view_id: String, + + #[pb(index = 2)] + pub row_id: String, #[pb(index = 3)] pub from_index: i32, @@ -79,30 +105,38 @@ pub struct MoveItemPayloadPB { pub to_index: i32, #[pb(index = 5)] - pub ty: MoveItemTypePB, + pub layout: GridLayout, + + #[pb(index = 6, one_of)] + pub upper_row_id: Option, } -#[derive(Clone)] -pub struct MoveItemParams { - pub grid_id: String, - pub item_id: String, +pub struct MoveRowParams { + pub view_id: String, + pub row_id: String, pub from_index: i32, pub to_index: i32, - pub ty: MoveItemTypePB, + pub layout: GridLayout, + pub upper_row_id: Option, } -impl TryInto for MoveItemPayloadPB { +impl TryInto for MoveRowPayloadPB { type Error = ErrorCode; - fn try_into(self) -> Result { - let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; - let item_id = NotEmptyStr::parse(self.item_id).map_err(|_| ErrorCode::InvalidData)?; - Ok(MoveItemParams { - grid_id: grid_id.0, - item_id: item_id.0, + fn try_into(self) -> Result { + let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::GridViewIdIsEmpty)?; + let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?; + let upper_row_id = match self.upper_row_id { + None => None, + Some(upper_row_id) => Some(NotEmptyStr::parse(upper_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0), + }; + Ok(MoveRowParams { + view_id: view_id.0, + row_id: row_id.0, from_index: self.from_index, to_index: self.to_index, - ty: self.ty, + layout: self.layout, + upper_row_id, }) } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs index 1ce2b40358..1ba3991f96 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs @@ -1,4 +1,4 @@ -use crate::entities::{CreateRowParams, RowPB}; +use crate::entities::{CreateRowParams, GridLayout}; use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; @@ -22,46 +22,7 @@ impl TryInto for CreateBoardCardPayloadPB { grid_id: grid_id.0, start_row_id: None, group_id: Some(group_id.0), + layout: GridLayout::Board, }) } } - -#[derive(Debug, Default, ProtoBuf)] -pub struct BoardCardChangesetPB { - #[pb(index = 1)] - pub group_id: String, - - #[pb(index = 2)] - pub inserted_cards: Vec, - - #[pb(index = 3)] - pub deleted_cards: Vec, - - #[pb(index = 4)] - pub updated_cards: Vec, -} -impl BoardCardChangesetPB { - pub fn insert(group_id: String, inserted_cards: Vec) -> Self { - Self { - group_id, - inserted_cards, - ..Default::default() - } - } - - pub fn delete(group_id: String, deleted_cards: Vec) -> Self { - Self { - group_id, - deleted_cards, - ..Default::default() - } - } - - pub fn update(group_id: String, updated_cards: Vec) -> Self { - Self { - group_id, - updated_cards, - ..Default::default() - } - } -} 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 new file mode 100644 index 0000000000..1feb0debe2 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs @@ -0,0 +1,43 @@ +use crate::entities::{InsertedRowPB, RowPB}; +use flowy_derive::ProtoBuf; + +#[derive(Debug, Default, ProtoBuf)] +pub struct GroupRowsChangesetPB { + #[pb(index = 1)] + pub group_id: String, + + #[pb(index = 2)] + pub inserted_rows: Vec, + + #[pb(index = 3)] + pub deleted_rows: Vec, + + #[pb(index = 4)] + pub updated_rows: Vec, +} + +impl GroupRowsChangesetPB { + pub fn insert(group_id: String, inserted_rows: Vec) -> Self { + Self { + group_id, + inserted_rows, + ..Default::default() + } + } + + pub fn delete(group_id: String, deleted_rows: Vec) -> Self { + Self { + group_id, + deleted_rows, + ..Default::default() + } + } + + pub fn update(group_id: String, updated_rows: Vec) -> Self { + Self { + group_id, + updated_rows, + ..Default::default() + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/mod.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/mod.rs index 7aa3a1a43e..f5daa803bc 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/mod.rs @@ -1,7 +1,9 @@ mod board_card; mod configuration; mod group; +mod group_changeset; pub use board_card::*; pub use configuration::*; pub use group::*; +pub use group_changeset::*; diff --git a/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs index d42052c747..398351371c 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs @@ -1,3 +1,4 @@ +use crate::entities::GridLayout; use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; @@ -46,7 +47,7 @@ pub struct BlockRowIdPB { } #[derive(ProtoBuf, Default)] -pub struct CreateRowPayloadPB { +pub struct CreateTableRowPayloadPB { #[pb(index = 1)] pub grid_id: String, @@ -59,17 +60,20 @@ pub struct CreateRowParams { pub grid_id: String, pub start_row_id: Option, pub group_id: Option, + pub layout: GridLayout, } -impl TryInto for CreateRowPayloadPB { +impl TryInto for CreateTableRowPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; + Ok(CreateRowParams { grid_id: grid_id.0, start_row_id: self.start_row_id, group_id: None, + layout: GridLayout::Table, }) } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs index e970249661..89f66dd433 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs @@ -19,7 +19,7 @@ pub struct GridSettingPB { pub layouts: Vec, #[pb(index = 2)] - pub current_layout_type: Layout, + pub current_layout_type: GridLayout, #[pb(index = 3)] pub filter_configuration_by_field_id: HashMap, @@ -34,13 +34,13 @@ pub struct GridSettingPB { #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct GridLayoutPB { #[pb(index = 1)] - ty: Layout, + ty: GridLayout, } impl GridLayoutPB { pub fn all() -> Vec { let mut layouts = vec![]; - for layout_ty in Layout::iter() { + for layout_ty in GridLayout::iter() { layouts.push(GridLayoutPB { ty: layout_ty }) } @@ -50,31 +50,31 @@ impl GridLayoutPB { #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)] #[repr(u8)] -pub enum Layout { +pub enum GridLayout { Table = 0, Board = 1, } -impl std::default::Default for Layout { +impl std::default::Default for GridLayout { fn default() -> Self { - Layout::Table + GridLayout::Table } } -impl std::convert::From for Layout { +impl std::convert::From for GridLayout { fn from(rev: LayoutRevision) -> Self { match rev { - LayoutRevision::Table => Layout::Table, - LayoutRevision::Board => Layout::Board, + LayoutRevision::Table => GridLayout::Table, + LayoutRevision::Board => GridLayout::Board, } } } -impl std::convert::From for LayoutRevision { - fn from(layout: Layout) -> Self { +impl std::convert::From for LayoutRevision { + fn from(layout: GridLayout) -> Self { match layout { - Layout::Table => LayoutRevision::Table, - Layout::Board => LayoutRevision::Board, + GridLayout::Table => LayoutRevision::Table, + GridLayout::Board => LayoutRevision::Board, } } } @@ -85,7 +85,7 @@ pub struct GridSettingChangesetPayloadPB { pub grid_id: String, #[pb(index = 2)] - pub layout_type: Layout, + pub layout_type: GridLayout, #[pb(index = 3, one_of)] pub insert_filter: Option, diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 3001ecb52d..df86f18fe3 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -203,13 +203,13 @@ pub(crate) async fn create_field_type_option_data_handler( } #[tracing::instrument(level = "trace", skip(data, manager), err)] -pub(crate) async fn move_item_handler( - data: Data, +pub(crate) async fn move_field_handler( + data: Data, manager: AppData>, ) -> Result<(), FlowyError> { - let params: MoveItemParams = data.into_inner().try_into()?; + let params: MoveFieldParams = data.into_inner().try_into()?; let editor = manager.get_grid_editor(¶ms.grid_id)?; - let _ = editor.move_item(params).await?; + let _ = editor.move_field(params).await?; Ok(()) } @@ -260,8 +260,19 @@ pub(crate) async fn duplicate_row_handler( } #[tracing::instrument(level = "debug", skip(data, manager), err)] -pub(crate) async fn create_row_handler( - data: Data, +pub(crate) async fn move_row_handler( + data: Data, + manager: AppData>, +) -> Result<(), FlowyError> { + let params: MoveRowParams = data.into_inner().try_into()?; + let editor = manager.get_grid_editor(¶ms.view_id)?; + let _ = editor.move_row(params).await?; + Ok(()) +} + +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn create_table_row_handler( + data: Data, manager: AppData>, ) -> DataResult { let params: CreateRowParams = data.into_inner().try_into()?; diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index 980087fee0..55ef3ff4db 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -20,14 +20,15 @@ pub fn create(grid_manager: Arc) -> Module { .event(GridEvent::DeleteField, delete_field_handler) .event(GridEvent::SwitchToField, switch_to_field_handler) .event(GridEvent::DuplicateField, duplicate_field_handler) - .event(GridEvent::MoveItem, move_item_handler) + .event(GridEvent::MoveField, move_field_handler) .event(GridEvent::GetFieldTypeOption, get_field_type_option_data_handler) .event(GridEvent::CreateFieldTypeOption, create_field_type_option_data_handler) // Row - .event(GridEvent::CreateRow, create_row_handler) + .event(GridEvent::CreateTableRow, create_table_row_handler) .event(GridEvent::GetRow, get_row_handler) .event(GridEvent::DeleteRow, delete_row_handler) .event(GridEvent::DuplicateRow, duplicate_row_handler) + .event(GridEvent::MoveRow, move_row_handler) // Cell .event(GridEvent::GetCell, get_cell_handler) .event(GridEvent::UpdateCell, update_cell_handler) @@ -130,8 +131,8 @@ pub enum GridEvent { /// [MoveItem] event is used to move an item. For the moment, Item has two types defined in /// [MoveItemTypePB]. - #[event(input = "MoveItemPayloadPB")] - MoveItem = 22, + #[event(input = "MoveFieldPayloadPB")] + MoveField = 22, /// [FieldTypeOptionIdPB] event is used to get the FieldTypeOption data for a specific field type. /// @@ -166,8 +167,8 @@ pub enum GridEvent { #[event(input = "SelectOptionChangesetPayloadPB")] UpdateSelectOption = 32, - #[event(input = "CreateRowPayloadPB", output = "RowPB")] - CreateRow = 50, + #[event(input = "CreateTableRowPayloadPB", output = "RowPB")] + CreateTableRow = 50, /// [GetRow] event is used to get the row data,[RowPB]. [OptionalRowPB] is a wrapper that enables /// to return a nullable row data. @@ -180,6 +181,9 @@ pub enum GridEvent { #[event(input = "RowIdPB")] DuplicateRow = 53, + #[event(input = "MoveRowPayloadPB")] + MoveRow = 54, + #[event(input = "GridCellIdPB", output = "GridCellPB")] GetCell = 70, diff --git a/frontend/rust-lib/flowy-grid/src/services/block_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs index a53866ee48..64fcc87072 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs @@ -59,7 +59,7 @@ impl GridBlockRevisionEditor { if let Some(start_row_id) = prev_row_id.as_ref() { match block_pad.index_of_row(start_row_id) { None => {} - Some(index) => row_index = Some(index + 1), + Some(index) => row_index = Some(index as i32 + 1), } } @@ -100,6 +100,10 @@ impl GridBlockRevisionEditor { Ok(()) } + pub async fn index_of_row(&self, row_id: &str) -> Option { + self.pad.read().await.index_of_row(row_id) + } + pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult>> { let row_ids = vec![Cow::Borrowed(row_id)]; let row_rev = self.get_row_revs(Some(row_ids)).await?.pop(); diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index 457215f8be..e6ada307db 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs @@ -52,7 +52,7 @@ impl GridBlockManager { } } - async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult> { + pub(crate) async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult> { let block_id = self.persistence.get_block_id(row_id)?; Ok(self.get_block_editor(&block_id).await?) } @@ -155,7 +155,7 @@ impl GridBlockManager { Ok(changesets) } - + // This function will be moved to GridViewRevisionEditor pub(crate) async fn move_row(&self, row_rev: Arc, from: usize, to: usize) -> FlowyResult<()> { let editor = self.get_editor_from_row_id(&row_rev.id).await?; let _ = editor.move_row(&row_rev.id, from, to).await?; @@ -180,6 +180,14 @@ impl GridBlockManager { Ok(()) } + // This function will be moved to GridViewRevisionEditor. + pub async fn index_of_row(&self, row_id: &str) -> Option { + match self.get_editor_from_row_id(row_id).await { + Ok(editor) => editor.index_of_row(row_id).await, + Err(_) => None, + } + } + pub async fn update_cell(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()> where F: FnOnce(Arc) -> RowPB, diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index d1aa57a2a0..c39bb9e929 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -342,7 +342,7 @@ impl GridRevisionEditor { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { let _ = self.block_manager.delete_row(row_id).await?; - self.view_manager.delete_row(row_id).await; + self.view_manager.did_delete_row(row_id).await; Ok(()) } @@ -484,21 +484,22 @@ impl GridRevisionEditor { Ok(snapshots) } - pub async fn move_item(&self, params: MoveItemParams) -> FlowyResult<()> { - match params.ty { - MoveItemTypePB::MoveField => { - self.move_field(¶ms.item_id, params.from_index, params.to_index) - .await - } - MoveItemTypePB::MoveRow => self.move_row(¶ms.item_id, params.from_index, params.to_index).await, - } + pub async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> { + self.view_manager.move_row(params).await } - pub async fn move_field(&self, field_id: &str, from: i32, to: i32) -> FlowyResult<()> { + pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> { + let MoveFieldParams { + grid_id: _, + field_id, + from_index, + to_index, + } = params; + let _ = self - .modify(|grid_pad| Ok(grid_pad.move_field(field_id, from as usize, to as usize)?)) + .modify(|grid_pad| Ok(grid_pad.move_field(&field_id, from_index as usize, to_index as usize)?)) .await?; - if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(field_id) { + if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(&field_id) { let delete_field_order = FieldIdPB::from(field_id); let insert_field = IndexFieldPB::from_field_rev(field_rev, index); let notified_changeset = FieldChangesetPB { @@ -513,10 +514,6 @@ impl GridRevisionEditor { Ok(()) } - pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { - self.view_manager.move_row(row_id, from, to).await - } - pub async fn delta_bytes(&self) -> Bytes { self.grid_pad.read().await.delta_bytes() } 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 937d7d725b..adb016ed41 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 @@ -1,13 +1,17 @@ use flowy_error::{FlowyError, FlowyResult}; -use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, RowPB}; +use crate::entities::{ + CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB, + RowPB, +}; use crate::services::grid_editor_task::GridServiceTaskScheduler; -use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService}; +use crate::services::group::{default_group_configuration, Group, GroupConfigurationDelegate, GroupService}; use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision}; use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad}; use flowy_sync::entities::revision::Revision; +use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::services::setting::make_grid_setting; use flowy_sync::entities::grid::GridSettingChangesetParams; use lib_infra::future::{wrap_future, AFFuture, FutureResult}; @@ -19,7 +23,7 @@ pub trait GridViewRevisionDelegate: Send + Sync + 'static { fn get_field_rev(&self, field_id: &str) -> AFFuture>>; } -pub trait GridViewRevisionDataSource: Send + Sync + 'static { +pub trait GridViewRevisionRowDataSource: Send + Sync + 'static { fn row_revs(&self) -> AFFuture>>; } @@ -30,8 +34,9 @@ pub struct GridViewRevisionEditor { pad: Arc>, rev_manager: Arc, delegate: Arc, - data_source: Arc, + data_source: Arc, group_service: Arc>, + groups: Arc>>, scheduler: Arc, } @@ -47,7 +52,7 @@ impl GridViewRevisionEditor { ) -> FlowyResult where Delegate: GridViewRevisionDelegate, - DataSource: GridViewRevisionDataSource, + DataSource: GridViewRevisionRowDataSource, { let cloud = Arc::new(GridViewRevisionCloudService { token: token.to_owned(), @@ -57,52 +62,87 @@ impl GridViewRevisionEditor { let rev_manager = Arc::new(rev_manager); let group_service = GroupService::new(Box::new(pad.clone())).await; let user_id = user_id.to_owned(); + let groups = Arc::new(RwLock::new(vec![])); Ok(Self { pad, user_id, view_id, rev_manager, scheduler, + groups, delegate: Arc::new(delegate), data_source: Arc::new(data_source), group_service: Arc::new(RwLock::new(group_service)), }) } - pub(crate) async fn create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { - match params.group_id.as_ref() { - None => {} - Some(group_id) => { - self.group_service - .read() - .await - .update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id)) - .await; + pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { + match params.layout { + GridLayout::Table => { + // Table can be grouped too } + GridLayout::Board => match params.group_id.as_ref() { + None => {} + Some(group_id) => { + self.group_service + .read() + .await + .update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id)) + .await; + } + }, } - todo!() } pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { + // Send the group notification if the current view has groups match params.group_id.as_ref() { None => {} Some(group_id) => { - self.group_service.read().await.did_create_row(group_id, row_pb).await; + let inserted_row = InsertedRowPB { + row: row_pb.clone(), + index: None, + }; + let changeset = GroupRowsChangesetPB::insert(group_id.clone(), vec![inserted_row]); + self.notify_did_update_group(changeset).await; } } } - pub(crate) async fn delete_row(&self, row_id: &str) { - self.group_service.read().await.did_delete_card(row_id.to_owned()).await; + pub(crate) async fn did_delete_row(&self, row_id: &str) { + // Send the group notification if the current view has groups; + match self.group_id_of_row(row_id).await { + None => {} + Some(group_id) => { + let changeset = GroupRowsChangesetPB::delete(group_id, vec![row_id.to_owned()]); + self.notify_did_update_group(changeset).await; + } + } + } + + async fn group_id_of_row(&self, row_id: &str) -> Option { + let read_guard = self.groups.read().await; + for group in read_guard.iter() { + if group.rows.iter().any(|row| row.id == row_id) { + return Some(group.id.clone()); + } + } + + None } pub(crate) async fn load_groups(&self) -> FlowyResult> { let field_revs = self.delegate.get_field_revs().await; let row_revs = self.data_source.row_revs().await; + + // let mut write_guard = self.group_service.write().await; match write_guard.load_groups(&field_revs, row_revs).await { None => Ok(vec![]), - Some(groups) => Ok(groups), + Some(groups) => { + *self.groups.write().await = groups.clone(); + Ok(groups.into_iter().map(GroupPB::from).collect()) + } } } @@ -129,6 +169,12 @@ impl GridViewRevisionEditor { } } + async fn notify_did_update_group(&self, changeset: GroupRowsChangesetPB) { + send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup) + .payload(changeset) + .send(); + } + async fn modify(&self, f: F) -> FlowyResult<()> where F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult>, diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs index cdaf72665e..edae70ba63 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs @@ -1,8 +1,12 @@ -use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB}; +use crate::entities::{ + CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, MoveRowParams, RepeatedGridGroupPB, RowPB, +}; use crate::manager::GridUser; use crate::services::block_manager::GridBlockManager; use crate::services::grid_editor_task::GridServiceTaskScheduler; -use crate::services::grid_view_editor::{GridViewRevisionDataSource, GridViewRevisionDelegate, GridViewRevisionEditor}; +use crate::services::grid_view_editor::{ + GridViewRevisionDelegate, GridViewRevisionEditor, GridViewRevisionRowDataSource, +}; use bytes::Bytes; use dashmap::DashMap; use flowy_error::FlowyResult; @@ -45,7 +49,7 @@ impl GridViewManager { pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { for view_editor in self.view_editors.iter() { - view_editor.create_row(row_rev, params).await; + view_editor.update_row(row_rev, params).await; } } @@ -55,9 +59,9 @@ impl GridViewManager { } } - pub(crate) async fn delete_row(&self, row_id: &str) { + pub(crate) async fn did_delete_row(&self, row_id: &str) { for view_editor in self.view_editors.iter() { - view_editor.delete_row(row_id).await; + view_editor.did_delete_row(row_id).await; } } @@ -83,15 +87,35 @@ impl GridViewManager { Ok(RepeatedGridGroupPB { items: groups }) } - pub(crate) async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { - match self.block_manager.get_row_rev(row_id).await? { + pub(crate) async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> { + let MoveRowParams { + view_id: _, + row_id, + from_index, + to_index, + layout, + upper_row_id, + } = params; + + let from_index = from_index as usize; + + match self.block_manager.get_row_rev(&row_id).await? { None => tracing::warn!("Move row failed, can not find the row:{}", row_id), - Some(row_rev) => { - let _ = self - .block_manager - .move_row(row_rev.clone(), from as usize, to as usize) - .await?; - } + Some(row_rev) => match layout { + GridLayout::Table => { + tracing::trace!("Move row from {} to {}", from_index, to_index); + let to_index = to_index as usize; + let _ = self.block_manager.move_row(row_rev, from_index, to_index).await?; + } + GridLayout::Board => { + if let Some(upper_row_id) = upper_row_id { + if let Some(to_index) = self.block_manager.index_of_row(&upper_row_id).await { + tracing::trace!("Move row from {} to {}", from_index, to_index); + let _ = self.block_manager.move_row(row_rev, from_index, to_index).await?; + } + } + } + }, } Ok(()) } @@ -132,7 +156,7 @@ async fn make_view_editor( ) -> FlowyResult where Delegate: GridViewRevisionDelegate, - DataSource: GridViewRevisionDataSource, + DataSource: GridViewRevisionRowDataSource, { tracing::trace!("Open view:{} editor", view_id); @@ -170,7 +194,7 @@ impl RevisionCompactor for GridViewRevisionCompactor { } } -impl GridViewRevisionDataSource for Arc { +impl GridViewRevisionRowDataSource for Arc { fn row_revs(&self) -> AFFuture>> { let block_manager = self.clone(); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs index 6ea0c78be2..01c2e7a2d9 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs @@ -22,7 +22,7 @@ impl GroupActionHandler for CheckboxGroupController { &self.field_id } - fn get_groups(&self) -> Vec { + fn build_groups(&self) -> Vec { self.make_groups() } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs index 0f15538acb..11144184a8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs @@ -28,7 +28,7 @@ pub trait Groupable { pub trait GroupActionHandler: Send + Sync { fn field_id(&self) -> &str; - fn get_groups(&self) -> Vec; + fn build_groups(&self) -> Vec; fn group_rows(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult<()>; fn update_card(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str); } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs index 933e3f936e..eb9ce69c9d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs @@ -30,7 +30,7 @@ impl GroupActionHandler for SingleSelectGroupController { &self.field_id } - fn get_groups(&self) -> Vec { + fn build_groups(&self) -> Vec { self.make_groups() } @@ -94,7 +94,7 @@ impl GroupActionHandler for MultiSelectGroupController { &self.field_id } - fn get_groups(&self) -> Vec { + fn build_groups(&self) -> Vec { self.make_groups() } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs index 266abee6d7..2fd4bed686 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs @@ -1,11 +1,9 @@ -use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::entities::{ - BoardCardChangesetPB, CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupPB, - NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, - UrlGroupConfigurationPB, + CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, NumberGroupConfigurationPB, + SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB, }; use crate::services::group::{ - CheckboxGroupController, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController, + CheckboxGroupController, Group, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController, }; use bytes::Bytes; use flowy_error::FlowyResult; @@ -36,7 +34,7 @@ impl GroupService { &mut self, field_revs: &[Arc], row_revs: Vec>, - ) -> Option> { + ) -> Option> { let field_rev = find_group_field(field_revs).unwrap(); let field_type: FieldType = field_rev.field_type_rev.into(); let configuration = self.delegate.get_group_configuration(field_rev.clone()).await; @@ -79,26 +77,6 @@ impl GroupService { // let row_pb = make_row_from_row_rev(row_rev); todo!() } - #[allow(dead_code)] - pub async fn did_delete_card(&self, _row_id: String) { - // let changeset = BoardCardChangesetPB::delete(group_id.to_owned(), vec![row_id]); - // self.notify_did_update_board(changeset).await; - todo!() - } - - pub async fn did_create_row(&self, group_id: &str, row_pb: &RowPB) { - let changeset = BoardCardChangesetPB::insert(group_id.to_owned(), vec![row_pb.clone()]); - self.notify_did_update_board(changeset).await; - } - - pub async fn notify_did_update_board(&self, changeset: BoardCardChangesetPB) { - if self.action_handler.is_none() { - return; - } - send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard) - .payload(changeset) - .send(); - } #[tracing::instrument(level = "trace", skip_all, err)] async fn build_groups( @@ -107,7 +85,7 @@ impl GroupService { field_rev: &Arc, row_revs: Vec>, configuration: GroupConfigurationRevision, - ) -> FlowyResult> { + ) -> FlowyResult> { match field_type { FieldType::RichText => { // let generator = GroupGenerator::::from_configuration(configuration); @@ -139,11 +117,11 @@ impl GroupService { if let Some(group_action_handler) = self.action_handler.as_ref() { let mut write_guard = group_action_handler.write().await; let _ = write_guard.group_rows(&row_revs, field_rev)?; - groups = write_guard.get_groups(); + groups = write_guard.build_groups(); drop(write_guard); } - Ok(groups.into_iter().map(GroupPB::from).collect()) + Ok(groups) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs b/frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs index 6eb481c74f..7d285fbe2b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs @@ -1,5 +1,5 @@ use crate::entities::{ - GridLayoutPB, GridSettingPB, Layout, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, + GridLayout, GridLayoutPB, GridSettingPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RepeatedGridSortPB, }; use flowy_grid_data_model::revision::{FieldRevision, SettingRevision}; @@ -12,7 +12,7 @@ pub struct GridSettingChangesetBuilder { } impl GridSettingChangesetBuilder { - pub fn new(grid_id: &str, layout_type: &Layout) -> Self { + pub fn new(grid_id: &str, layout_type: &GridLayout) -> Self { let params = GridSettingChangesetParams { grid_id: grid_id.to_string(), layout_type: layout_type.clone().into(), @@ -42,7 +42,7 @@ impl GridSettingChangesetBuilder { } pub fn make_grid_setting(grid_setting_rev: &SettingRevision, field_revs: &[Arc]) -> GridSettingPB { - let current_layout_type: Layout = grid_setting_rev.layout.clone().into(); + let current_layout_type: GridLayout = grid_setting_rev.layout.clone().into(); let filters_by_field_id = grid_setting_rev .get_all_filters(field_revs) .map(|filters_by_field_id| { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 2c54f8561e..54a022e0d6 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow}; use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, RowPB}; +use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, GridLayout, RowPB}; use flowy_grid::services::field::*; use flowy_grid_data_model::revision::{ GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, @@ -81,6 +81,7 @@ impl GridRowTest { grid_id: self.editor.grid_id.clone(), start_row_id: None, group_id: None, + layout: GridLayout::Table, }; let row_order = self.editor.create_row(params).await.unwrap(); self.row_order_by_row_id diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs index 03ba9fa29c..3320204ae3 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] #![allow(unused_imports)] -use flowy_grid::entities::{CreateGridFilterPayloadPB, Layout, GridSettingPB}; +use flowy_grid::entities::{CreateGridFilterPayloadPB, GridLayout, GridSettingPB}; use flowy_grid::services::setting::GridSettingChangesetBuilder; use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision}; use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams}; @@ -55,7 +55,7 @@ impl GridFilterTest { } FilterScript::InsertGridTableFilter { payload } => { let params: CreateGridFilterParams = payload.try_into().unwrap(); - let layout_type = Layout::Table; + let layout_type = GridLayout::Table; let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) .insert_filter(params) .build(); @@ -66,7 +66,7 @@ impl GridFilterTest { assert_eq!(count as usize, filters.len()); } FilterScript::DeleteGridTableFilter { filter_id, field_rev} => { - let layout_type = Layout::Table; + let layout_type = GridLayout::Table; let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) .delete_filter(DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.field_type_rev }) .build(); diff --git a/shared-lib/flowy-error-code/src/code.rs b/shared-lib/flowy-error-code/src/code.rs index 5b676073da..63f7ac7749 100644 --- a/shared-lib/flowy-error-code/src/code.rs +++ b/shared-lib/flowy-error-code/src/code.rs @@ -91,6 +91,9 @@ pub enum ErrorCode { #[display(fmt = "Grid id is empty")] GridIdIsEmpty = 410, + #[display(fmt = "Grid view id is empty")] + GridViewIdIsEmpty = 411, + #[display(fmt = "Grid block id is empty")] BlockIdIsEmpty = 420, #[display(fmt = "Row id is empty")] diff --git a/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs index 4215da7572..142463b8fc 100644 --- a/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs @@ -139,12 +139,8 @@ impl GridBlockRevisionPad { self.block.rows.len() as i32 } - pub fn index_of_row(&self, row_id: &str) -> Option { - self.block - .rows - .iter() - .position(|row| row.id == row_id) - .map(|index| index as i32) + pub fn index_of_row(&self, row_id: &str) -> Option { + self.block.rows.iter().position(|row| row.id == row_id) } pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult> {