diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index e70b9ffb97..0efc79b00e 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -29,7 +29,7 @@ "program": "./lib/main.dart", "type": "dart", "env": { - "RUST_LOG": "trace" + "RUST_LOG": "debug" }, "cwd": "${workspaceRoot}/app_flowy" }, 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 ac72b6e24e..f84c2a2bd1 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -23,7 +23,7 @@ class BoardBloc extends Bloc { final BoardDataController _dataController; late final AFBoardDataController afBoardDataController; final MoveRowFFIService _rowService; - Map groupControllers = {}; + LinkedHashMap groupControllers = LinkedHashMap.new(); GridFieldCache get fieldCache => _dataController.fieldCache; String get gridId => _dataController.gridId; @@ -34,9 +34,13 @@ class BoardBloc extends Bloc { super(BoardState.initial(view.id)) { afBoardDataController = AFBoardDataController( onMoveColumn: ( + fromColumnId, fromIndex, + toColumnId, toIndex, - ) {}, + ) { + _moveGroup(fromColumnId, toColumnId); + }, onMoveColumnItem: ( columnId, fromIndex, @@ -44,7 +48,7 @@ class BoardBloc extends Bloc { ) { final fromRow = groupControllers[columnId]?.rowAtIndex(fromIndex); final toRow = groupControllers[columnId]?.rowAtIndex(toIndex); - _moveRow(fromRow, toRow); + _moveRow(fromRow, columnId, toRow); }, onMoveColumnItemToColumn: ( fromColumnId, @@ -54,7 +58,7 @@ class BoardBloc extends Bloc { ) { final fromRow = groupControllers[fromColumnId]?.rowAtIndex(fromIndex); final toRow = groupControllers[toColumnId]?.rowAtIndex(toIndex); - _moveRow(fromRow, toRow); + _moveRow(fromRow, toColumnId, toRow); }, ); @@ -95,12 +99,13 @@ class BoardBloc extends Bloc { ); } - void _moveRow(RowPB? fromRow, RowPB? toRow) { - if (fromRow != null && toRow != null) { + void _moveRow(RowPB? fromRow, String columnId, RowPB? toRow) { + if (fromRow != null) { _rowService - .moveRow( + .moveGroupRow( fromRowId: fromRow.id, - toRowId: toRow.id, + toGroupId: columnId, + toRowId: toRow?.id, ) .then((result) { result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r))); @@ -108,6 +113,17 @@ class BoardBloc extends Bloc { } } + void _moveGroup(String fromColumnId, String toColumnId) { + _rowService + .moveGroup( + fromGroupId: fromColumnId, + toGroupId: toColumnId, + ) + .then((result) { + result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r))); + }); + } + @override Future close() async { await _dataController.dispose(); diff --git a/frontend/app_flowy/lib/plugins/board/application/group_controller.dart b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart index 3f545dae3b..6fd68b1df8 100644 --- a/frontend/app_flowy/lib/plugins/board/application/group_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart @@ -37,6 +37,14 @@ class GroupController { (GroupRowsChangesetPB changeset) { for (final insertedRow in changeset.insertedRows) { final index = insertedRow.hasIndex() ? insertedRow.index : null; + + if (insertedRow.hasIndex() && + group.rows.length > insertedRow.index) { + group.rows.insert(insertedRow.index, insertedRow.row); + } else { + group.rows.add(insertedRow.row); + } + delegate.insertRow( group.groupId, insertedRow.row, @@ -45,10 +53,19 @@ class GroupController { } for (final deletedRow in changeset.deletedRows) { + group.rows.removeWhere((rowPB) => rowPB.id == deletedRow); delegate.removeRow(group.groupId, deletedRow); } for (final updatedRow in changeset.updatedRows) { + final index = group.rows.indexWhere( + (rowPB) => rowPB.id == updatedRow.id, + ); + + if (index != -1) { + group.rows[index] = updatedRow; + } + delegate.updateRow(group.groupId, updatedRow); } }, 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 8c46ce18b2..c8b6873d91 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart @@ -3,7 +3,6 @@ import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/board_card.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart'; 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 a18c0c8e75..2612f5975c 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 @@ -3,6 +3,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart'; 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/group_changeset.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; class RowFFIService { @@ -68,4 +69,33 @@ class MoveRowFFIService { return GridEventMoveRow(payload).send(); } + + Future> moveGroupRow({ + required String fromRowId, + required String toGroupId, + required String? toRowId, + }) { + var payload = MoveGroupRowPayloadPB.create() + ..viewId = gridId + ..fromRowId = fromRowId + ..toGroupId = toGroupId; + + if (toRowId != null) { + payload.toRowId = toRowId; + } + + return GridEventMoveGroupRow(payload).send(); + } + + Future> moveGroup({ + required String fromGroupId, + required String toGroupId, + }) { + final payload = MoveGroupPayloadPB.create() + ..viewId = gridId + ..fromGroupId = fromGroupId + ..toGroupId = toGroupId; + + return GridEventMoveGroup(payload).send(); + } } 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 83f75d2a0e..01aba725a5 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 @@ -10,7 +10,7 @@ class MultiBoardListExample extends StatefulWidget { class _MultiBoardListExampleState extends State { final AFBoardDataController boardDataController = AFBoardDataController( - onMoveColumn: (fromIndex, toIndex) { + onMoveColumn: (fromColumnId, fromIndex, toColumnId, toIndex) { debugPrint('Move column from $fromIndex to $toIndex'); }, onMoveColumnItem: (columnId, fromIndex, toIndex) { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart index 6e184761c5..f26bd16c50 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart @@ -145,7 +145,8 @@ class AFBoardColumnData extends ReoderFlexItem with EquatableMixin { }) : _items = items; /// Returns the readonly List - UnmodifiableListView get items => UnmodifiableListView(_items); + UnmodifiableListView get items => + UnmodifiableListView([..._items]); @override List get props => [id, ..._items]; 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 6208dbd0f0..e8d5471939 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 @@ -8,7 +8,12 @@ import 'reorder_flex/reorder_flex.dart'; import 'package:flutter/material.dart'; import 'reorder_phantom/phantom_controller.dart'; -typedef OnMoveColumn = void Function(int fromIndex, int toIndex); +typedef OnMoveColumn = void Function( + String fromColumnId, + int fromIndex, + String toColumnId, + int toIndex, +); typedef OnMoveColumnItem = void Function( String columnId, @@ -98,9 +103,11 @@ class AFBoardDataController extends ChangeNotifier } void moveColumn(int fromIndex, int toIndex, {bool notify = true}) { - final columnData = _columnDatas.removeAt(fromIndex); - _columnDatas.insert(toIndex, columnData); - onMoveColumn?.call(fromIndex, toIndex); + final toColumnData = _columnDatas[toIndex]; + final fromColumnData = _columnDatas.removeAt(fromIndex); + + _columnDatas.insert(toIndex, fromColumnData); + onMoveColumn?.call(fromColumnData.id, fromIndex, toColumnData.id, toIndex); if (notify) notifyListeners(); } diff --git a/frontend/rust-lib/flowy-error/src/errors.rs b/frontend/rust-lib/flowy-error/src/errors.rs index e19ebb15b2..bc453054aa 100644 --- a/frontend/rust-lib/flowy-error/src/errors.rs +++ b/frontend/rust-lib/flowy-error/src/errors.rs @@ -66,6 +66,7 @@ impl FlowyError { static_flowy_error!(user_not_exist, ErrorCode::UserNotExist); static_flowy_error!(text_too_long, ErrorCode::TextTooLong); static_flowy_error!(invalid_data, ErrorCode::InvalidData); + static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds); } impl std::convert::From for FlowyError { diff --git a/frontend/rust-lib/flowy-grid/src/dart_notification.rs b/frontend/rust-lib/flowy-grid/src/dart_notification.rs index 0bba5bbc11..a0030c6773 100644 --- a/frontend/rust-lib/flowy-grid/src/dart_notification.rs +++ b/frontend/rust-lib/flowy-grid/src/dart_notification.rs @@ -11,7 +11,8 @@ pub enum GridNotification { DidUpdateRow = 30, DidUpdateCell = 40, DidUpdateField = 50, - DidUpdateGroup = 60, + DidUpdateGroupView = 60, + DidUpdateGroup = 61, } impl std::default::Default for GridNotification { diff --git a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs index 8875b3fc26..271cbcf424 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs @@ -2,7 +2,6 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision}; -use flowy_sync::entities::grid::FieldChangesetParams; use serde_repr::*; use std::sync::Arc; @@ -491,6 +490,27 @@ impl TryInto for FieldChangesetPayloadPB { } } +#[derive(Debug, Clone, Default)] +pub struct FieldChangesetParams { + pub field_id: String, + + pub grid_id: String, + + pub name: Option, + + pub desc: Option, + + pub field_type: Option, + + pub frozen: Option, + + pub visibility: Option, + + pub width: Option, + + pub type_option_data: Option>, +} + #[derive( Debug, Clone, diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs index 079b6fd6dc..7dff00bf56 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs @@ -5,8 +5,7 @@ use crate::entities::{ use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; -use flowy_grid_data_model::revision::{FieldRevision, FilterConfigurationRevision}; -use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams}; +use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision, FilterConfigurationRevision}; use std::convert::TryInto; use std::sync::Arc; @@ -72,6 +71,12 @@ impl TryInto for DeleteFilterPayloadPB { } } +pub struct DeleteFilterParams { + pub field_id: String, + pub filter_id: String, + pub field_type_rev: FieldTypeRevision, +} + #[derive(ProtoBuf, Debug, Default, Clone)] pub struct CreateGridFilterPayloadPB { #[pb(index = 1)] @@ -99,10 +104,10 @@ impl CreateGridFilterPayloadPB { } } -impl TryInto for CreateGridFilterPayloadPB { +impl TryInto for CreateGridFilterPayloadPB { type Error = ErrorCode; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let field_id = NotEmptyStr::parse(self.field_id) .map_err(|_| ErrorCode::FieldIdIsEmpty)? .0; @@ -125,7 +130,7 @@ impl TryInto for CreateGridFilterPayloadPB { } } - Ok(CreateGridFilterParams { + Ok(CreateFilterParams { field_id, field_type_rev: self.field_type.into(), condition, @@ -133,3 +138,10 @@ impl TryInto for CreateGridFilterPayloadPB { }) } } + +pub struct CreateFilterParams { + pub field_id: String, + pub field_type_rev: FieldTypeRevision, + pub condition: u8, + pub content: Option, +} 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 be8cfdeae1..c012376f55 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs @@ -123,3 +123,46 @@ impl TryInto for MoveRowPayloadPB { }) } } +#[derive(Debug, Clone, Default, ProtoBuf)] +pub struct MoveGroupRowPayloadPB { + #[pb(index = 1)] + pub view_id: String, + + #[pb(index = 2)] + pub from_row_id: String, + + #[pb(index = 3)] + pub to_group_id: String, + + #[pb(index = 4, one_of)] + pub to_row_id: Option, +} + +pub struct MoveGroupRowParams { + pub view_id: String, + pub from_row_id: String, + pub to_group_id: String, + pub to_row_id: Option, +} + +impl TryInto for MoveGroupRowPayloadPB { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::GridViewIdIsEmpty)?; + let from_row_id = NotEmptyStr::parse(self.from_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?; + let to_group_id = NotEmptyStr::parse(self.to_group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?; + + let to_row_id = match self.to_row_id { + None => None, + Some(to_row_id) => Some(NotEmptyStr::parse(to_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0), + }; + + Ok(MoveGroupRowParams { + view_id: view_id.0, + from_row_id: from_row_id.0, + to_group_id: to_group_id.0, + to_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 deleted file mode 100644 index 1ba3991f96..0000000000 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::entities::{CreateRowParams, GridLayout}; -use flowy_derive::ProtoBuf; -use flowy_error::ErrorCode; -use flowy_grid_data_model::parser::NotEmptyStr; - -#[derive(ProtoBuf, Debug, Default, Clone)] -pub struct CreateBoardCardPayloadPB { - #[pb(index = 1)] - pub grid_id: String, - - #[pb(index = 2)] - pub group_id: String, -} - -impl TryInto for CreateBoardCardPayloadPB { - type Error = ErrorCode; - - fn try_into(self) -> Result { - let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; - let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?; - Ok(CreateRowParams { - grid_id: grid_id.0, - start_row_id: None, - group_id: Some(group_id.0), - layout: GridLayout::Board, - }) - } -} diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/configuration.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/configuration.rs index baa39d91a2..cb5503727b 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/configuration.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/configuration.rs @@ -1,4 +1,5 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; +use flowy_grid_data_model::revision::{GroupRecordRevision, SelectOptionGroupConfigurationRevision}; #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct UrlGroupConfigurationPB { @@ -18,6 +19,32 @@ pub struct SelectOptionGroupConfigurationPB { hide_empty: bool, } +impl std::convert::From for SelectOptionGroupConfigurationPB { + fn from(rev: SelectOptionGroupConfigurationRevision) -> Self { + Self { + hide_empty: rev.hide_empty, + } + } +} + +#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] +pub struct GroupRecordPB { + #[pb(index = 1)] + group_id: String, + + #[pb(index = 2)] + visible: bool, +} + +impl std::convert::From for GroupRecordPB { + fn from(rev: GroupRecordRevision) -> Self { + Self { + group_id: rev.group_id, + visible: rev.visible, + } + } +} + #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct NumberGroupConfigurationPB { #[pb(index = 1)] diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs index 32f7c4543a..b769b18154 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs @@ -1,12 +1,35 @@ -use crate::entities::{FieldType, RowPB}; +use crate::entities::{CreateRowParams, FieldType, GridLayout, RowPB}; use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; -use flowy_grid_data_model::revision::GroupConfigurationRevision; -use flowy_sync::entities::grid::{CreateGridGroupParams, DeleteGroupParams}; +use flowy_grid_data_model::revision::{FieldTypeRevision, GroupConfigurationRevision}; use std::convert::TryInto; use std::sync::Arc; +#[derive(ProtoBuf, Debug, Default, Clone)] +pub struct CreateBoardCardPayloadPB { + #[pb(index = 1)] + pub grid_id: String, + + #[pb(index = 2)] + pub group_id: String, +} + +impl TryInto for CreateBoardCardPayloadPB { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; + let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?; + Ok(CreateRowParams { + grid_id: grid_id.0, + start_row_id: None, + group_id: Some(group_id.0), + layout: GridLayout::Board, + }) + } +} + #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] pub struct GridGroupConfigurationPB { #[pb(index = 1)] @@ -86,27 +109,28 @@ pub struct CreateGridGroupPayloadPB { #[pb(index = 2)] pub field_type: FieldType, - - #[pb(index = 3, one_of)] - pub content: Option>, } -impl TryInto for CreateGridGroupPayloadPB { +impl TryInto for CreateGridGroupPayloadPB { type Error = ErrorCode; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let field_id = NotEmptyStr::parse(self.field_id) .map_err(|_| ErrorCode::FieldIdIsEmpty)? .0; - Ok(CreateGridGroupParams { + Ok(CreatGroupParams { field_id, field_type_rev: self.field_type.into(), - content: self.content, }) } } +pub struct CreatGroupParams { + pub field_id: String, + pub field_type_rev: FieldTypeRevision, +} + #[derive(ProtoBuf, Debug, Default, Clone)] pub struct DeleteGroupPayloadPB { #[pb(index = 1)] @@ -137,3 +161,9 @@ impl TryInto for DeleteGroupPayloadPB { }) } } + +pub struct DeleteGroupParams { + pub field_id: String, + pub group_id: String, + pub field_type_rev: FieldTypeRevision, +} 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 743da70444..a3ebee9cb7 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 @@ -1,5 +1,7 @@ -use crate::entities::{InsertedRowPB, RowPB}; +use crate::entities::{GroupPB, InsertedRowPB, RowPB}; use flowy_derive::ProtoBuf; +use flowy_error::ErrorCode; +use flowy_grid_data_model::parser::NotEmptyStr; use std::fmt::Formatter; #[derive(Debug, Default, ProtoBuf)] @@ -62,3 +64,64 @@ impl GroupRowsChangesetPB { } } } +#[derive(Debug, Default, ProtoBuf)] +pub struct MoveGroupPayloadPB { + #[pb(index = 1)] + pub view_id: String, + + #[pb(index = 2)] + pub from_group_id: String, + + #[pb(index = 3)] + pub to_group_id: String, +} + +pub struct MoveGroupParams { + pub view_id: String, + pub from_group_id: String, + pub to_group_id: String, +} + +impl TryInto for MoveGroupPayloadPB { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let view_id = NotEmptyStr::parse(self.view_id) + .map_err(|_| ErrorCode::GridViewIdIsEmpty)? + .0; + let from_group_id = NotEmptyStr::parse(self.from_group_id) + .map_err(|_| ErrorCode::GroupIdIsEmpty)? + .0; + let to_group_id = NotEmptyStr::parse(self.to_group_id) + .map_err(|_| ErrorCode::GroupIdIsEmpty)? + .0; + Ok(MoveGroupParams { + view_id, + from_group_id, + to_group_id, + }) + } +} + +#[derive(Debug, Default, ProtoBuf)] +pub struct GroupViewChangesetPB { + #[pb(index = 1)] + pub view_id: String, + + #[pb(index = 2)] + pub inserted_groups: Vec, + + #[pb(index = 3)] + pub deleted_groups: Vec, +} + +impl GroupViewChangesetPB {} + +#[derive(Debug, Default, ProtoBuf)] +pub struct InsertedGroupPB { + #[pb(index = 1)] + pub group: GroupPB, + + #[pb(index = 2)] + pub index: i32, +} 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 f5daa803bc..778eff4cc9 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,9 +1,7 @@ -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/mod.rs b/frontend/rust-lib/flowy-grid/src/entities/mod.rs index 96c7a9ba60..eb11d982a4 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/mod.rs @@ -6,7 +6,6 @@ mod grid_entities; mod group_entities; mod row_entities; mod setting_entities; -mod sort_entities; pub use block_entities::*; pub use cell_entities::*; @@ -16,4 +15,3 @@ pub use grid_entities::*; pub use group_entities::*; pub use row_entities::*; pub use setting_entities::*; -pub use sort_entities::*; 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 89f66dd433..9c02a2c692 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs @@ -1,12 +1,12 @@ use crate::entities::{ - CreateGridFilterPayloadPB, CreateGridGroupPayloadPB, CreateGridSortPayloadPB, DeleteFilterPayloadPB, - DeleteGroupPayloadPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RepeatedGridSortPB, + CreatGroupParams, CreateFilterParams, CreateGridFilterPayloadPB, CreateGridGroupPayloadPB, DeleteFilterParams, + DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, RepeatedGridConfigurationFilterPB, + RepeatedGridGroupConfigurationPB, }; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::revision::LayoutRevision; -use flowy_sync::entities::grid::GridSettingChangesetParams; use std::collections::HashMap; use std::convert::TryInto; use strum::IntoEnumIterator; @@ -26,9 +26,6 @@ pub struct GridSettingPB { #[pb(index = 4)] pub group_configuration_by_field_id: HashMap, - - #[pb(index = 5)] - pub sorts_by_field_id: HashMap, } #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] @@ -98,12 +95,6 @@ pub struct GridSettingChangesetPayloadPB { #[pb(index = 6, one_of)] pub delete_group: Option, - - #[pb(index = 7, one_of)] - pub insert_sort: Option, - - #[pb(index = 8, one_of)] - pub delete_sort: Option, } impl TryInto for GridSettingChangesetPayloadPB { @@ -134,16 +125,6 @@ impl TryInto for GridSettingChangesetPayloadPB { None => None, }; - let insert_sort = match self.insert_sort { - None => None, - Some(payload) => Some(payload.try_into()?), - }; - - let delete_sort = match self.delete_sort { - None => None, - Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0), - }; - Ok(GridSettingChangesetParams { grid_id: view_id, layout_type: self.layout_type.into(), @@ -151,8 +132,21 @@ impl TryInto for GridSettingChangesetPayloadPB { delete_filter, insert_group, delete_group, - insert_sort, - delete_sort, }) } } + +pub struct GridSettingChangesetParams { + pub grid_id: String, + pub layout_type: LayoutRevision, + pub insert_filter: Option, + pub delete_filter: Option, + pub insert_group: Option, + pub delete_group: Option, +} + +impl GridSettingChangesetParams { + pub fn is_filter_changed(&self) -> bool { + self.insert_filter.is_some() || self.delete_filter.is_some() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs deleted file mode 100644 index f844b75066..0000000000 --- a/frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs +++ /dev/null @@ -1,65 +0,0 @@ -use flowy_derive::ProtoBuf; -use flowy_error::ErrorCode; -use flowy_grid_data_model::parser::NotEmptyStr; -use flowy_grid_data_model::revision::SortConfigurationRevision; -use flowy_sync::entities::grid::CreateGridSortParams; -use std::convert::TryInto; -use std::sync::Arc; - -#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] -pub struct GridSort { - #[pb(index = 1)] - pub id: String, - - #[pb(index = 2, one_of)] - pub field_id: Option, -} - -impl std::convert::From<&SortConfigurationRevision> for GridSort { - fn from(rev: &SortConfigurationRevision) -> Self { - GridSort { - id: rev.id.clone(), - - field_id: rev.field_id.clone(), - } - } -} - -#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] -pub struct RepeatedGridSortPB { - #[pb(index = 1)] - pub items: Vec, -} - -impl std::convert::From>> for RepeatedGridSortPB { - fn from(revs: Vec>) -> Self { - RepeatedGridSortPB { - items: revs.into_iter().map(|rev| rev.as_ref().into()).collect(), - } - } -} - -impl std::convert::From> for RepeatedGridSortPB { - fn from(items: Vec) -> Self { - Self { items } - } -} - -#[derive(ProtoBuf, Debug, Default, Clone)] -pub struct CreateGridSortPayloadPB { - #[pb(index = 1, one_of)] - pub field_id: Option, -} - -impl TryInto for CreateGridSortPayloadPB { - type Error = ErrorCode; - - fn try_into(self) -> Result { - let field_id = match self.field_id { - None => None, - Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0), - }; - - Ok(CreateGridSortParams { field_id }) - } -} diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 3108acd977..a9060aa87e 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -10,7 +10,6 @@ use crate::services::field::{ use crate::services::row::make_row_from_row_rev; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::FieldRevision; -use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams}; use lib_dispatch::prelude::{data_result, AppData, Data, DataResult}; use std::sync::Arc; @@ -36,17 +35,6 @@ pub(crate) async fn get_grid_setting_handler( data_result(grid_setting) } -#[tracing::instrument(level = "trace", skip(data, manager), err)] -pub(crate) async fn update_grid_setting_handler( - data: Data, - manager: AppData>, -) -> Result<(), FlowyError> { - let params: GridSettingChangesetParams = data.into_inner().try_into()?; - let editor = manager.open_grid(¶ms.grid_id).await?; - let _ = editor.update_grid_setting(params).await?; - Ok(()) -} - #[tracing::instrument(level = "debug", skip(data, manager), err)] pub(crate) async fn get_grid_blocks_handler( data: Data, @@ -437,3 +425,25 @@ pub(crate) async fn create_board_card_handler( let row = editor.create_row(params).await?; data_result(row) } + +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn move_group_handler( + data: Data, + manager: AppData>, +) -> FlowyResult<()> { + let params: MoveGroupParams = data.into_inner().try_into()?; + let editor = manager.get_grid_editor(params.view_id.as_ref())?; + let _ = editor.move_group(params).await?; + Ok(()) +} + +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn move_group_row_handler( + data: Data, + manager: AppData>, +) -> FlowyResult<()> { + let params: MoveGroupRowParams = data.into_inner().try_into()?; + let editor = manager.get_grid_editor(params.view_id.as_ref())?; + let _ = editor.move_group_row(params).await?; + Ok(()) +} diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index 55ef3ff4db..a78bcb5ed3 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -11,7 +11,7 @@ pub fn create(grid_manager: Arc) -> Module { .event(GridEvent::GetGrid, get_grid_handler) .event(GridEvent::GetGridBlocks, get_grid_blocks_handler) .event(GridEvent::GetGridSetting, get_grid_setting_handler) - .event(GridEvent::UpdateGridSetting, update_grid_setting_handler) + // .event(GridEvent::UpdateGridSetting, update_grid_setting_handler) // Field .event(GridEvent::GetFields, get_fields_handler) .event(GridEvent::UpdateField, update_field_handler) @@ -41,6 +41,8 @@ pub fn create(grid_manager: Arc) -> Module { .event(GridEvent::UpdateDateCell, update_date_cell_handler) // Group .event(GridEvent::CreateBoardCard, create_board_card_handler) + .event(GridEvent::MoveGroup, move_group_handler) + .event(GridEvent::MoveGroupRow, move_group_row_handler) .event(GridEvent::GetGroup, get_groups_handler); module @@ -217,4 +219,10 @@ pub enum GridEvent { #[event(input = "CreateBoardCardPayloadPB", output = "RowPB")] CreateBoardCard = 110, + + #[event(input = "MoveGroupPayloadPB")] + MoveGroup = 111, + + #[event(input = "MoveGroupRowPayloadPB")] + MoveGroupRow = 112, } diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 9e4556b793..12b94ff8db 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -131,7 +131,6 @@ impl GridManager { async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult> { match self.grid_editors.get(grid_id) { None => { - tracing::trace!("Create grid editor with id: {}", grid_id); let db_pool = self.grid_user.db_pool()?; let editor = self.make_grid_rev_editor(grid_id, db_pool).await?; diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs index a2831c0ad9..8335a36fb2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs @@ -4,7 +4,7 @@ #![allow(unused_imports)] #![allow(unused_results)] use crate::dart_notification::{send_dart_notification, GridNotification}; -use crate::entities::{FieldType, GridBlockChangesetPB}; +use crate::entities::{FieldType, GridBlockChangesetPB, GridSettingChangesetParams}; use crate::services::block_manager::GridBlockManager; use crate::services::cell::{AnyCellData, CellFilterOperation}; use crate::services::field::{ @@ -20,7 +20,6 @@ use crate::services::tasks::{FilterTaskContext, Task, TaskContent}; use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision}; use flowy_sync::client_grid::GridRevisionPad; -use flowy_sync::entities::grid::GridSettingChangesetParams; use rayon::prelude::*; use std::collections::HashMap; use std::sync::Arc; 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 92cf9da550..485bf6930c 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -15,7 +15,6 @@ use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::*; use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeserializer}; -use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams}; use flowy_sync::entities::revision::Revision; use flowy_sync::errors::CollaborateResult; use flowy_sync::util::make_text_delta_from_revisions; @@ -96,23 +95,19 @@ impl GridRevisionEditor { } = params; let field_id = field.id.clone(); if self.contain_field(&field_id).await { - let _ = self - .modify(|grid| { - let deserializer = TypeOptionJsonDeserializer(field.field_type.clone()); - let changeset = FieldChangesetParams { - field_id: field.id, - grid_id, - name: Some(field.name), - desc: Some(field.desc), - field_type: Some(field.field_type.into()), - frozen: Some(field.frozen), - visibility: Some(field.visibility), - width: Some(field.width), - type_option_data: Some(type_option_data), - }; - Ok(grid.update_field_rev(changeset, deserializer)?) - }) - .await?; + let changeset = FieldChangesetParams { + field_id: field.id, + grid_id, + name: Some(field.name), + desc: Some(field.desc), + field_type: Some(field.field_type.clone().into()), + frozen: Some(field.frozen), + visibility: Some(field.visibility), + width: Some(field.width), + type_option_data: Some(type_option_data), + }; + + let _ = self.update_field_rev(changeset, field.field_type).await?; let _ = self.notify_did_update_grid_field(&field_id).await?; } else { let _ = self @@ -140,19 +135,13 @@ impl GridRevisionEditor { return Ok(()); } let field_rev = result.unwrap(); - let _ = self - .modify(|grid| { - let field_type = field_rev.ty.into(); - let deserializer = TypeOptionJsonDeserializer(field_type); - let changeset = FieldChangesetParams { - field_id: field_id.to_owned(), - grid_id: grid_id.to_owned(), - type_option_data: Some(type_option_data), - ..Default::default() - }; - Ok(grid.update_field_rev(changeset, deserializer)?) - }) - .await?; + let changeset = FieldChangesetParams { + field_id: field_id.to_owned(), + grid_id: grid_id.to_owned(), + type_option_data: Some(type_option_data), + ..Default::default() + }; + let _ = self.update_field_rev(changeset, field_rev.ty.into()).await?; let _ = self.notify_did_update_grid_field(field_id).await?; Ok(()) } @@ -179,17 +168,21 @@ impl GridRevisionEditor { pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> { let field_id = params.field_id.clone(); - let json_deserializer = match self.grid_pad.read().await.get_field_rev(params.field_id.as_str()) { - None => return Err(ErrorCode::FieldDoesNotExist.into()), - Some((_, field_rev)) => TypeOptionJsonDeserializer(field_rev.ty.into()), - }; + let field_type: Option = self + .grid_pad + .read() + .await + .get_field_rev(params.field_id.as_str()) + .map(|(_, field_rev)| field_rev.ty.into()); - let _ = self - .modify(|grid| Ok(grid.update_field_rev(params, json_deserializer)?)) - .await?; - - let _ = self.notify_did_update_grid_field(&field_id).await?; - Ok(()) + match field_type { + None => Err(ErrorCode::FieldDoesNotExist.into()), + Some(field_type) => { + let _ = self.update_field_rev(params, field_type).await?; + let _ = self.notify_did_update_grid_field(&field_id).await?; + Ok(()) + } + } } pub async fn replace_field(&self, field_rev: Arc) -> FlowyResult<()> { @@ -269,6 +262,62 @@ impl GridRevisionEditor { Ok(field_revs) } + async fn update_field_rev(&self, params: FieldChangesetParams, field_type: FieldType) -> FlowyResult<()> { + self.modify(|grid| { + let deserializer = TypeOptionJsonDeserializer(field_type); + + let changeset = grid.modify_field(¶ms.field_id, |field| { + let mut is_changed = None; + if let Some(name) = params.name { + field.name = name; + is_changed = Some(()) + } + + if let Some(desc) = params.desc { + field.desc = desc; + is_changed = Some(()) + } + + if let Some(field_type) = params.field_type { + field.ty = field_type; + is_changed = Some(()) + } + + if let Some(frozen) = params.frozen { + field.frozen = frozen; + is_changed = Some(()) + } + + if let Some(visibility) = params.visibility { + field.visibility = visibility; + is_changed = Some(()) + } + + if let Some(width) = params.width { + field.width = width; + is_changed = Some(()) + } + + if let Some(type_option_data) = params.type_option_data { + match deserializer.deserialize(type_option_data) { + Ok(json_str) => { + let field_type = field.ty; + field.insert_type_option_str(&field_type, json_str); + is_changed = Some(()) + } + Err(err) => { + tracing::error!("Deserialize data to type option json failed: {}", err); + } + } + } + + Ok(is_changed) + })?; + Ok(changeset) + }) + .await + } + pub async fn create_block(&self, block_meta_rev: GridBlockMetaRevision) -> FlowyResult<()> { let _ = self .modify(|grid_pad| Ok(grid_pad.create_block_meta_rev(block_meta_rev)?)) @@ -294,6 +343,11 @@ impl GridRevisionEditor { Ok(row_pb) } + pub async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { + let _ = self.view_manager.move_group(params).await?; + Ok(()) + } + pub async fn insert_rows(&self, row_revs: Vec) -> FlowyResult> { let block_id = self.block_id().await?; let mut rows_by_block_id: HashMap> = HashMap::new(); @@ -460,8 +514,13 @@ impl GridRevisionEditor { self.view_manager.get_filters().await } - pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> { - let _ = self.view_manager.update_setting(params).await?; + pub async fn update_filter(&self, params: CreateFilterParams) -> FlowyResult<()> { + let _ = self.view_manager.update_filter(params).await?; + Ok(()) + } + + pub async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> { + let _ = self.view_manager.delete_filter(params).await?; Ok(()) } @@ -501,16 +560,6 @@ impl GridRevisionEditor { .block_manager .move_row(row_rev.clone(), from_index, to_index) .await?; - - if let Some(row_changeset) = self.view_manager.move_row(row_rev, to_row_id.clone()).await { - tracing::trace!("Receive row changeset after moving the row"); - match self.block_manager.update_row(row_changeset).await { - Ok(_) => {} - Err(e) => { - tracing::error!("Apply row changeset error:{:?}", e); - } - } - } } (_, None) => tracing::warn!("Can not find the from row id: {}", from_row_id), (None, _) => tracing::warn!("Can not find the to row id: {}", to_row_id), @@ -520,6 +569,35 @@ impl GridRevisionEditor { Ok(()) } + pub async fn move_group_row(&self, params: MoveGroupRowParams) -> FlowyResult<()> { + let MoveGroupRowParams { + view_id: _, + from_row_id, + to_group_id, + to_row_id, + } = params; + + match self.block_manager.get_row_rev(&from_row_id).await? { + None => tracing::warn!("Move row failed, can not find the row:{}", from_row_id), + Some(row_rev) => { + if let Some(row_changeset) = self + .view_manager + .move_group_row(row_rev, to_group_id, to_row_id.clone()) + .await + { + match self.block_manager.update_row(row_changeset).await { + Ok(_) => {} + Err(e) => { + tracing::error!("Apply row changeset error:{:?}", e); + } + } + } + } + } + + Ok(()) + } + pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> { let MoveFieldParams { grid_id: _, 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 da3ea1be2d..4e23d9be19 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,18 +1,23 @@ use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::entities::{ - CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB, RowPB, + CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridLayout, GridLayoutPB, + GridSettingPB, GroupPB, GroupRowsChangesetPB, GroupViewChangesetPB, InsertedGroupPB, InsertedRowPB, + MoveGroupParams, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RowPB, }; use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate}; -use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService}; -use crate::services::setting::make_grid_setting; +use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter, GroupService}; use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision}; +use flowy_grid_data_model::revision::{ + gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision, + RowChangeset, RowRevision, +}; use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad}; -use flowy_sync::entities::grid::GridSettingChangesetParams; use flowy_sync::entities::revision::Revision; use lib_infra::future::{wrap_future, AFFuture, FutureResult}; +use std::collections::HashMap; + use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use tokio::sync::RwLock; @@ -31,6 +36,7 @@ pub struct GridViewRevisionEditor { } impl GridViewRevisionEditor { + #[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn new( user_id: &str, token: &str, @@ -46,7 +52,14 @@ impl GridViewRevisionEditor { let view_revision_pad = rev_manager.load::(Some(cloud)).await?; let pad = Arc::new(RwLock::new(view_revision_pad)); let rev_manager = Arc::new(rev_manager); - let group_service = GroupService::new(Box::new(pad.clone())).await; + + let configuration_reader = GroupConfigurationReaderImpl(pad.clone()); + let configuration_writer = GroupConfigurationWriterImpl { + user_id: user_id.to_owned(), + rev_manager: rev_manager.clone(), + view_pad: pad.clone(), + }; + let group_service = GroupService::new(configuration_reader, configuration_writer).await; let user_id = user_id.to_owned(); let did_load_group = AtomicBool::new(false); Ok(Self { @@ -67,7 +80,7 @@ impl GridViewRevisionEditor { None => {} Some(group_id) => { self.group_service - .read() + .write() .await .will_create_row(row_rev, group_id, |field_id| { self.field_delegate.get_field_rev(&field_id) @@ -87,7 +100,7 @@ impl GridViewRevisionEditor { index: None, }; let changeset = GroupRowsChangesetPB::insert(group_id.clone(), vec![inserted_row]); - self.notify_did_update_group(changeset).await; + self.notify_did_update_group_rows(changeset).await; } } } @@ -102,7 +115,7 @@ impl GridViewRevisionEditor { .await { for changeset in changesets { - self.notify_did_update_group(changeset).await; + self.notify_did_update_group_rows(changeset).await; } } } @@ -116,38 +129,40 @@ impl GridViewRevisionEditor { .await { for changeset in changesets { - self.notify_did_update_group(changeset).await; + self.notify_did_update_group_rows(changeset).await; } } } - pub(crate) async fn did_move_row( + pub(crate) async fn move_group_row( &self, row_rev: &RowRevision, row_changeset: &mut RowChangeset, - upper_row_id: &str, + to_group_id: &str, + to_row_id: Option, ) { if let Some(changesets) = self .group_service .write() .await - .did_move_row(row_rev, row_changeset, upper_row_id, |field_id| { + .move_group_row(row_rev, row_changeset, to_group_id, to_row_id, |field_id| { self.field_delegate.get_field_rev(&field_id) }) .await { for changeset in changesets { - tracing::trace!("Group: {} changeset: {}", changeset.group_id, changeset); - self.notify_did_update_group(changeset).await; + self.notify_did_update_group_rows(changeset).await; } } } + #[tracing::instrument(level = "trace", skip(self))] pub(crate) async fn load_groups(&self) -> FlowyResult> { let groups = if !self.did_load_group.load(Ordering::SeqCst) { self.did_load_group.store(true, Ordering::SeqCst); let field_revs = self.field_delegate.get_field_revs().await; let row_revs = self.row_delegate.gv_row_revs().await; + match self .group_service .write() @@ -162,23 +177,47 @@ impl GridViewRevisionEditor { self.group_service.read().await.groups().await }; + tracing::trace!("Number of groups: {}", groups.len()); Ok(groups.into_iter().map(GroupPB::from).collect()) } + pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { + let _ = self + .group_service + .write() + .await + .move_group(¶ms.from_group_id, ¶ms.to_group_id) + .await?; + + match self.group_service.read().await.get_group(¶ms.from_group_id).await { + None => {} + Some((index, group)) => { + let inserted_group = InsertedGroupPB { + group: GroupPB::from(group), + index: index as i32, + }; + + let changeset = GroupViewChangesetPB { + view_id: "".to_string(), + inserted_groups: vec![inserted_group], + deleted_groups: vec![params.from_group_id.clone()], + }; + + self.notify_did_update_view(changeset).await; + } + } + Ok(()) + } + pub(crate) async fn get_setting(&self) -> GridSettingPB { let field_revs = self.field_delegate.get_field_revs().await; - let grid_setting = make_grid_setting(self.pad.read().await.get_setting_rev(), &field_revs); + let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs); grid_setting } - pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> { - let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await; - Ok(()) - } - pub(crate) async fn get_filters(&self) -> Vec { let field_revs = self.field_delegate.get_field_revs().await; - match self.pad.read().await.get_setting_rev().get_all_filters(&field_revs) { + match self.pad.read().await.get_all_filters(&field_revs) { None => vec![], Some(filters) => filters .into_values() @@ -188,12 +227,45 @@ impl GridViewRevisionEditor { } } - async fn notify_did_update_group(&self, changeset: GroupRowsChangesetPB) { + pub(crate) async fn insert_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> { + self.modify(|pad| { + let filter_rev = FilterConfigurationRevision { + id: gen_grid_filter_id(), + field_id: insert_filter.field_id.clone(), + condition: insert_filter.condition, + content: insert_filter.content, + }; + let changeset = pad.insert_filter(&insert_filter.field_id, &insert_filter.field_type_rev, filter_rev)?; + Ok(changeset) + }) + .await + } + + pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> { + self.modify(|pad| { + let changeset = pad.delete_filter( + &delete_filter.field_id, + &delete_filter.field_type_rev, + &delete_filter.filter_id, + )?; + Ok(changeset) + }) + .await + } + + async fn notify_did_update_group_rows(&self, changeset: GroupRowsChangesetPB) { send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup) .payload(changeset) .send(); } + async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) { + send_dart_notification(&self.view_id, GridNotification::DidUpdateGroupView) + .payload(changeset) + .send(); + } + + #[allow(dead_code)] async fn modify(&self, f: F) -> FlowyResult<()> where F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult>, @@ -202,28 +274,24 @@ impl GridViewRevisionEditor { match f(&mut *write_guard)? { None => {} Some(change) => { - let _ = self.apply_change(change).await?; + let _ = apply_change(&self.user_id, self.rev_manager.clone(), change).await?; } } Ok(()) } +} - async fn apply_change(&self, change: GridViewRevisionChangeset) -> FlowyResult<()> { - let GridViewRevisionChangeset { delta, md5 } = change; - let user_id = self.user_id.clone(); - let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); - let delta_data = delta.json_bytes(); - let revision = Revision::new( - &self.rev_manager.object_id, - base_rev_id, - rev_id, - delta_data, - &user_id, - md5, - ); - let _ = self.rev_manager.add_local_revision(&revision).await?; - Ok(()) - } +async fn apply_change( + user_id: &str, + rev_manager: Arc, + change: GridViewRevisionChangeset, +) -> FlowyResult<()> { + let GridViewRevisionChangeset { delta, md5 } = change; + let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair(); + let delta_data = delta.json_bytes(); + let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5); + let _ = rev_manager.add_local_revision(&revision).await?; + Ok(()) } struct GridViewRevisionCloudService { @@ -248,19 +316,97 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder { } } -impl GroupConfigurationDelegate for Arc> { - fn get_group_configuration(&self, field_rev: Arc) -> AFFuture { - let view_pad = self.clone(); +struct GroupConfigurationReaderImpl(Arc>); + +impl GroupConfigurationReader for GroupConfigurationReaderImpl { + fn get_group_configuration( + &self, + field_rev: Arc, + ) -> AFFuture>> { + let view_pad = self.0.clone(); wrap_future(async move { - let grid_pad = view_pad.read().await; - let configurations = grid_pad.get_groups(&field_rev.id, &field_rev.ty); - match configurations { - None => default_group_configuration(&field_rev), - Some(mut configurations) => { - assert_eq!(configurations.len(), 1); - (&*configurations.pop().unwrap()).clone() - } + let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?; + if groups.is_empty() { + None + } else { + debug_assert_eq!(groups.len(), 1); + Some(groups.pop().unwrap()) } }) } } + +struct GroupConfigurationWriterImpl { + user_id: String, + rev_manager: Arc, + view_pad: Arc>, +} + +impl GroupConfigurationWriter for GroupConfigurationWriterImpl { + fn save_group_configuration( + &self, + field_id: &str, + field_type: FieldTypeRevision, + group_configuration: GroupConfigurationRevision, + ) -> AFFuture> { + let user_id = self.user_id.clone(); + let rev_manager = self.rev_manager.clone(); + let view_pad = self.view_pad.clone(); + let field_id = field_id.to_owned(); + + wrap_future(async move { + let changeset = view_pad + .write() + .await + .insert_group(&field_id, &field_type, group_configuration)?; + + if let Some(changeset) = changeset { + let _ = apply_change(&user_id, rev_manager, changeset).await?; + } + Ok(()) + }) + } +} + +pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc]) -> GridSettingPB { + let current_layout_type: GridLayout = view_pad.layout.clone().into(); + let filters_by_field_id = view_pad + .get_all_filters(field_revs) + .map(|filters_by_field_id| { + filters_by_field_id + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect::>() + }) + .unwrap_or_default(); + let groups_by_field_id = view_pad + .get_all_groups(field_revs) + .map(|groups_by_field_id| { + groups_by_field_id + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect::>() + }) + .unwrap_or_default(); + + GridSettingPB { + layouts: GridLayoutPB::all(), + current_layout_type, + filter_configuration_by_field_id: filters_by_field_id, + group_configuration_by_field_id: groups_by_field_id, + } +} + +#[cfg(test)] +mod tests { + use lib_ot::core::TextDelta; + + #[test] + fn test() { + let s1 = r#"[{"insert":"{\"view_id\":\"fTURELffPr\",\"grid_id\":\"fTURELffPr\",\"layout\":0,\"filters\":[],\"groups\":[]}"}]"#; + let _delta_1 = TextDelta::from_json(s1).unwrap(); + + let s2 = r#"[{"retain":195},{"insert":"{\\\"group_id\\\":\\\"wD9i\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"xZtv\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"tFV2\\\",\\\"visible\\\":true}"},{"retain":10}]"#; + let _delta_2 = TextDelta::from_json(s2).unwrap(); + } +} 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 5a8faad6a0..b0e578f804 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,4 +1,7 @@ -use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB}; +use crate::entities::{ + CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridSettingPB, MoveGroupParams, + RepeatedGridGroupPB, RowPB, +}; use crate::manager::GridUser; use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_view_editor::GridViewRevisionEditor; @@ -8,7 +11,6 @@ use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; use flowy_revision::disk::SQLiteGridViewRevisionPersistence; use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence}; -use flowy_sync::entities::grid::GridSettingChangesetParams; use flowy_sync::entities::revision::Revision; use flowy_sync::util::make_text_delta_from_revisions; use lib_infra::future::AFFuture; @@ -97,30 +99,47 @@ impl GridViewManager { Ok(view_editor.get_setting().await) } - pub(crate) async fn update_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> { - let view_editor = self.get_default_view_editor().await?; - let _ = view_editor.update_setting(params).await?; - Ok(()) - } - pub(crate) async fn get_filters(&self) -> FlowyResult> { let view_editor = self.get_default_view_editor().await?; Ok(view_editor.get_filters().await) } + pub(crate) async fn update_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> { + let view_editor = self.get_default_view_editor().await?; + view_editor.insert_filter(insert_filter).await + } + + pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> { + let view_editor = self.get_default_view_editor().await?; + view_editor.delete_filter(delete_filter).await + } + pub(crate) async fn load_groups(&self) -> FlowyResult { let view_editor = self.get_default_view_editor().await?; let groups = view_editor.load_groups().await?; Ok(RepeatedGridGroupPB { items: groups }) } + pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { + let view_editor = self.get_default_view_editor().await?; + let _ = view_editor.move_group(params).await?; + Ok(()) + } + /// It may generate a RowChangeset when the Row was moved from one group to another. /// The return value, [RowChangeset], contains the changes made by the groups. /// - pub(crate) async fn move_row(&self, row_rev: Arc, to_row_id: String) -> Option { + pub(crate) async fn move_group_row( + &self, + row_rev: Arc, + to_group_id: String, + to_row_id: Option, + ) -> Option { let mut row_changeset = RowChangeset::new(row_rev.id.clone()); for view_editor in self.view_editors.iter() { - view_editor.did_move_row(&row_rev, &mut row_changeset, &to_row_id).await; + view_editor + .move_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone()) + .await; } if row_changeset.has_changed() { @@ -163,12 +182,11 @@ async fn make_view_editor( row_delegate: Arc, scheduler: Arc, ) -> FlowyResult { - tracing::trace!("Open view:{} editor", view_id); - let rev_manager = make_grid_view_rev_manager(user, view_id).await?; let user_id = user.user_id()?; let token = user.token()?; let view_id = view_id.to_owned(); + GridViewRevisionEditor::new( &user_id, &token, @@ -182,7 +200,6 @@ async fn make_view_editor( } pub async fn make_grid_view_rev_manager(user: &Arc, view_id: &str) -> FlowyResult { - tracing::trace!("Open view:{} editor", view_id); let user_id = user.user_id()?; let pool = user.db_pool()?; diff --git a/frontend/rust-lib/flowy-grid/src/services/group/action.rs b/frontend/rust-lib/flowy-grid/src/services/group/action.rs new file mode 100644 index 0000000000..29dc51cc37 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/action.rs @@ -0,0 +1,17 @@ +use crate::entities::GroupRowsChangesetPB; + +use crate::services::group::controller::MoveGroupRowContext; +use flowy_grid_data_model::revision::RowRevision; + +pub trait GroupAction: Send + Sync { + type CellDataType; + fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; + fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; + fn remove_row_if_match( + &mut self, + row_rev: &RowRevision, + cell_data: &Self::CellDataType, + ) -> Vec; + + fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec; +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs new file mode 100644 index 0000000000..a462278d2b --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs @@ -0,0 +1,221 @@ +use crate::services::group::{default_group_configuration, Group}; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_grid_data_model::revision::{ + FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRecordRevision, +}; +use std::marker::PhantomData; + +use indexmap::IndexMap; +use lib_infra::future::AFFuture; +use std::sync::Arc; + +pub trait GroupConfigurationReader: Send + Sync + 'static { + fn get_group_configuration( + &self, + field_rev: Arc, + ) -> AFFuture>>; +} + +pub trait GroupConfigurationWriter: Send + Sync + 'static { + fn save_group_configuration( + &self, + field_id: &str, + field_type: FieldTypeRevision, + group_configuration: GroupConfigurationRevision, + ) -> AFFuture>; +} + +pub struct GenericGroupConfiguration { + pub configuration: Arc, + configuration_content: PhantomData, + field_rev: Arc, + groups_map: IndexMap, + writer: Arc, +} + +impl GenericGroupConfiguration +where + C: GroupConfigurationContentSerde, +{ + #[tracing::instrument(level = "trace", skip_all, err)] + pub async fn new( + field_rev: Arc, + reader: Arc, + writer: Arc, + ) -> FlowyResult { + let configuration = match reader.get_group_configuration(field_rev.clone()).await { + None => { + let default_group_configuration = default_group_configuration(&field_rev); + writer + .save_group_configuration(&field_rev.id, field_rev.ty, default_group_configuration.clone()) + .await?; + Arc::new(default_group_configuration) + } + Some(configuration) => configuration, + }; + + // let configuration = C::from_configuration_content(&configuration_rev.content)?; + Ok(Self { + field_rev, + groups_map: IndexMap::new(), + writer, + configuration, + configuration_content: PhantomData, + }) + } + + pub(crate) fn groups(&self) -> Vec<&Group> { + self.groups_map.values().collect() + } + + pub(crate) fn clone_groups(&self) -> Vec { + self.groups_map.values().cloned().collect() + } + + pub(crate) async fn merge_groups(&mut self, groups: Vec) -> FlowyResult<()> { + let (group_revs, groups) = merge_groups(&self.configuration.groups, groups); + self.mut_configuration(move |configuration| { + configuration.groups = group_revs; + true + })?; + + groups.into_iter().for_each(|group| { + self.groups_map.insert(group.id.clone(), group); + }); + Ok(()) + } + + #[allow(dead_code)] + pub(crate) async fn hide_group(&mut self, group_id: &str) -> FlowyResult<()> { + self.mut_configuration_group(group_id, |group_rev| { + group_rev.visible = false; + })?; + Ok(()) + } + + #[allow(dead_code)] + pub(crate) async fn show_group(&mut self, group_id: &str) -> FlowyResult<()> { + self.mut_configuration_group(group_id, |group_rev| { + group_rev.visible = true; + })?; + Ok(()) + } + + pub(crate) fn with_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) { + self.groups_map.iter_mut().for_each(|(_, group)| { + each(group); + }) + } + + pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> { + self.groups_map.get_mut(group_id) + } + + pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> { + let from_index = self.groups_map.get_index_of(from_id); + let to_index = self.groups_map.get_index_of(to_id); + match (from_index, to_index) { + (Some(from_index), Some(to_index)) => { + self.groups_map.swap_indices(from_index, to_index); + + self.mut_configuration(|configuration| { + let from_index = configuration.groups.iter().position(|group| group.group_id == from_id); + let to_index = configuration.groups.iter().position(|group| group.group_id == to_id); + if let (Some(from), Some(to)) = (from_index, to_index) { + configuration.groups.swap(from, to); + } + true + })?; + Ok(()) + } + _ => Err(FlowyError::out_of_bounds()), + } + } + + // Returns the index and group specified by the group_id + pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> { + match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) { + (Some(index), Some(group)) => Some((index, group)), + _ => None, + } + } + + pub fn save_configuration(&self) -> FlowyResult<()> { + let configuration = (&*self.configuration).clone(); + let writer = self.writer.clone(); + let field_id = self.field_rev.id.clone(); + let field_type = self.field_rev.ty; + tokio::spawn(async move { + match writer + .save_group_configuration(&field_id, field_type, configuration) + .await + { + Ok(_) => {} + Err(e) => { + tracing::error!("Save group configuration failed: {}", e); + } + } + }); + + Ok(()) + } + + fn mut_configuration_group( + &mut self, + group_id: &str, + mut_groups_fn: impl Fn(&mut GroupRecordRevision), + ) -> FlowyResult<()> { + self.mut_configuration(|configuration| { + match configuration.groups.iter_mut().find(|group| group.group_id == group_id) { + None => false, + Some(group_rev) => { + mut_groups_fn(group_rev); + true + } + } + }) + } + + fn mut_configuration( + &mut self, + mut_configuration_fn: impl FnOnce(&mut GroupConfigurationRevision) -> bool, + ) -> FlowyResult<()> { + let configuration = Arc::make_mut(&mut self.configuration); + let is_changed = mut_configuration_fn(configuration); + if is_changed { + let _ = self.save_configuration()?; + } + Ok(()) + } +} + +fn merge_groups(old_group_revs: &[GroupRecordRevision], groups: Vec) -> (Vec, Vec) { + if old_group_revs.is_empty() { + let new_groups = groups + .iter() + .map(|group| GroupRecordRevision::new(group.id.clone())) + .collect(); + return (new_groups, groups); + } + + let mut group_map: IndexMap = IndexMap::new(); + groups.into_iter().for_each(|group| { + group_map.insert(group.id.clone(), group); + }); + + // Inert + let mut sorted_groups: Vec = vec![]; + for group_rev in old_group_revs { + if let Some(group) = group_map.remove(&group_rev.group_id) { + sorted_groups.push(group); + } + } + sorted_groups.extend(group_map.into_values().collect::>()); + let new_group_revs = sorted_groups + .iter() + .map(|group| GroupRecordRevision::new(group.id.clone())) + .collect::>(); + + tracing::trace!("group revs: {}, groups: {}", new_group_revs.len(), sorted_groups.len()); + (new_group_revs, sorted_groups) +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs new file mode 100644 index 0000000000..1a5ee23694 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -0,0 +1,214 @@ +use crate::entities::{GroupRowsChangesetPB, RowPB}; +use crate::services::cell::{decode_any_cell_data, CellBytesParser}; +use crate::services::group::action::GroupAction; +use crate::services::group::configuration::GenericGroupConfiguration; +use crate::services::group::entities::Group; +use flowy_error::FlowyResult; +use flowy_grid_data_model::revision::{ + FieldRevision, GroupConfigurationContentSerde, RowChangeset, RowRevision, TypeOptionDataDeserializer, +}; + +use std::marker::PhantomData; +use std::sync::Arc; + +const DEFAULT_GROUP_ID: &str = "default_group"; + +// Each kind of group must implement this trait to provide custom group +// operations. For example, insert cell data to the row_rev when creating +// a new row. +pub trait GroupController: GroupControllerSharedOperation + Send + Sync { + fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str); +} + +pub trait GroupGenerator { + type ConfigurationType; + type TypeOptionType; + + fn generate_groups( + field_id: &str, + configuration: &Self::ConfigurationType, + type_option: &Option, + ) -> Vec; +} + +pub struct MoveGroupRowContext<'a> { + pub row_rev: &'a RowRevision, + pub row_changeset: &'a mut RowChangeset, + pub field_rev: &'a FieldRevision, + pub to_group_id: &'a str, + pub to_row_id: Option, +} + +// Defines the shared actions each group controller can perform. +pub trait GroupControllerSharedOperation: Send + Sync { + // The field that is used for grouping the rows + fn field_id(&self) -> &str; + fn groups(&self) -> Vec; + fn get_group(&self, group_id: &str) -> Option<(usize, Group)>; + fn fill_groups(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult>; + fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>; + fn did_update_row( + &mut self, + row_rev: &RowRevision, + field_rev: &FieldRevision, + ) -> FlowyResult>; + + fn did_delete_row( + &mut self, + row_rev: &RowRevision, + field_rev: &FieldRevision, + ) -> FlowyResult>; + + fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult>; +} + +/// C: represents the group configuration that impl [GroupConfigurationSerde] +/// T: the type option data deserializer that impl [TypeOptionDataDeserializer] +/// G: the group generator, [GroupGenerator] +/// P: the parser that impl [CellBytesParser] for the CellBytes +pub struct GenericGroupController { + pub field_id: String, + pub type_option: Option, + pub configuration: GenericGroupConfiguration, + /// default_group is used to store the rows that don't belong to any groups. + default_group: Group, + group_action_phantom: PhantomData, + cell_parser_phantom: PhantomData

, +} + +impl GenericGroupController +where + C: GroupConfigurationContentSerde, + T: TypeOptionDataDeserializer, + G: GroupGenerator, TypeOptionType = T>, +{ + pub async fn new( + field_rev: &Arc, + mut configuration: GenericGroupConfiguration, + ) -> FlowyResult { + let field_type_rev = field_rev.ty; + let type_option = field_rev.get_type_option_entry::(field_type_rev); + let groups = G::generate_groups(&field_rev.id, &configuration, &type_option); + let _ = configuration.merge_groups(groups).await?; + let default_group = Group::new( + DEFAULT_GROUP_ID.to_owned(), + field_rev.id.clone(), + format!("No {}", field_rev.name), + "".to_string(), + ); + + Ok(Self { + field_id: field_rev.id.clone(), + default_group, + type_option, + configuration, + group_action_phantom: PhantomData, + cell_parser_phantom: PhantomData, + }) + } +} + +impl GroupControllerSharedOperation for GenericGroupController +where + P: CellBytesParser, + C: GroupConfigurationContentSerde, + Self: GroupAction, +{ + fn field_id(&self) -> &str { + &self.field_id + } + + fn groups(&self) -> Vec { + self.configuration.clone_groups() + } + + fn get_group(&self, group_id: &str) -> Option<(usize, Group)> { + let group = self.configuration.get_group(group_id)?; + Some((group.0, group.1.clone())) + } + + fn fill_groups(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult> { + for row_rev in row_revs { + if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { + let mut group_rows: Vec = vec![]; + let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); + let cell_data = cell_bytes.parser::

()?; + for group in self.configuration.groups() { + if self.can_group(&group.content, &cell_data) { + group_rows.push(GroupRow { + row: row_rev.into(), + group_id: group.id.clone(), + }); + } + } + + if group_rows.is_empty() { + self.default_group.add_row(row_rev.into()); + } else { + for group_row in group_rows { + if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) { + group.add_row(group_row.row); + } + } + } + } else { + self.default_group.add_row(row_rev.into()); + } + } + + let default_group = self.default_group.clone(); + let mut groups: Vec = self.configuration.clone_groups(); + if !default_group.number_of_row() == 0 { + groups.push(default_group); + } + + Ok(groups) + } + + fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> { + self.configuration.move_group(from_group_id, to_group_id) + } + + fn did_update_row( + &mut self, + row_rev: &RowRevision, + field_rev: &FieldRevision, + ) -> FlowyResult> { + if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { + let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); + let cell_data = cell_bytes.parser::

()?; + Ok(self.add_row_if_match(row_rev, &cell_data)) + } else { + Ok(vec![]) + } + } + + fn did_delete_row( + &mut self, + row_rev: &RowRevision, + field_rev: &FieldRevision, + ) -> FlowyResult> { + if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { + let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); + let cell_data = cell_bytes.parser::

()?; + Ok(self.remove_row_if_match(row_rev, &cell_data)) + } else { + Ok(vec![]) + } + } + + fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult> { + if let Some(cell_rev) = context.row_rev.cells.get(&self.field_id) { + let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), context.field_rev); + let cell_data = cell_bytes.parser::

()?; + Ok(self.move_row(&cell_data, context)) + } else { + Ok(vec![]) + } + } +} + +struct GroupRow { + row: RowPB, + group_id: String, +} 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/controller_impls/checkbox_controller.rs similarity index 68% rename from frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs rename to frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs index c1284dd659..ffcbf117fe 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs @@ -1,20 +1,25 @@ -use crate::entities::{CheckboxGroupConfigurationPB, GroupRowsChangesetPB}; - -use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; - +use crate::entities::GroupRowsChangesetPB; use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK}; -use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable}; +use crate::services::group::action::GroupAction; +use crate::services::group::configuration::GenericGroupConfiguration; +use crate::services::group::controller::{ + GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, +}; +use crate::services::group::entities::Group; + +use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowRevision}; pub type CheckboxGroupController = GenericGroupController< - CheckboxGroupConfigurationPB, + CheckboxGroupConfigurationRevision, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser, >; -impl Groupable for CheckboxGroupController { - type CellDataType = CheckboxCellData; +pub type CheckboxGroupConfiguration = GenericGroupConfiguration; +impl GroupAction for CheckboxGroupController { + type CellDataType = CheckboxCellData; fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool { false } @@ -35,13 +40,10 @@ impl Groupable for CheckboxGroupController { todo!() } - fn move_row_if_match( + fn move_row( &mut self, - _field_rev: &FieldRevision, - _row_rev: &RowRevision, - _row_changeset: &mut RowChangeset, _cell_data: &Self::CellDataType, - _to_row_id: &str, + _context: MoveGroupRowContext, ) -> Vec { todo!() } @@ -55,12 +57,12 @@ impl GroupController for CheckboxGroupController { pub struct CheckboxGroupGenerator(); impl GroupGenerator for CheckboxGroupGenerator { - type ConfigurationType = CheckboxGroupConfigurationPB; + type ConfigurationType = CheckboxGroupConfiguration; type TypeOptionType = CheckboxTypeOptionPB; fn generate_groups( field_id: &str, - _configuration: &Option, + _configuration: &Self::ConfigurationType, _type_option: &Option, ) -> Vec { let check_group = Group::new( diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/mod.rs new file mode 100644 index 0000000000..974f311a48 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/mod.rs @@ -0,0 +1,5 @@ +mod checkbox_controller; +mod select_option_controller; + +pub use checkbox_controller::*; +pub use select_option_controller::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs new file mode 100644 index 0000000000..0d7b8fa03e --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs @@ -0,0 +1,7 @@ +mod multi_select_controller; +mod single_select_controller; +mod util; + +pub use multi_select_controller::*; +pub use single_select_controller::*; +pub use util::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs new file mode 100644 index 0000000000..cce2698158 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -0,0 +1,98 @@ +use crate::entities::GroupRowsChangesetPB; +use crate::services::cell::insert_select_option_cell; +use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser}; +use crate::services::group::action::GroupAction; + +use crate::services::group::controller::{ + GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, +}; +use crate::services::group::controller_impls::select_option_controller::util::*; +use crate::services::group::entities::Group; +use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision}; + +// MultiSelect +pub type MultiSelectGroupController = GenericGroupController< + SelectOptionGroupConfigurationRevision, + MultiSelectTypeOptionPB, + MultiSelectGroupGenerator, + SelectOptionCellDataParser, +>; + +impl GroupAction for MultiSelectGroupController { + type CellDataType = SelectOptionCellDataPB; + + fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { + cell_data.select_options.iter().any(|option| option.id == content) + } + + fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { + let mut changesets = vec![]; + self.configuration.with_mut_groups(|group| { + add_row(group, &mut changesets, cell_data, row_rev); + }); + changesets + } + + fn remove_row_if_match( + &mut self, + row_rev: &RowRevision, + cell_data: &Self::CellDataType, + ) -> Vec { + let mut changesets = vec![]; + self.configuration.with_mut_groups(|group| { + remove_row(group, &mut changesets, cell_data, row_rev); + }); + changesets + } + + fn move_row( + &mut self, + cell_data: &Self::CellDataType, + mut context: MoveGroupRowContext, + ) -> Vec { + let mut group_changeset = vec![]; + self.configuration.with_mut_groups(|group| { + move_select_option_row(group, &mut group_changeset, cell_data, &mut context); + }); + group_changeset + } +} + +impl GroupController for MultiSelectGroupController { + fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) { + match self.configuration.get_group(group_id) { + None => tracing::warn!("Can not find the group: {}", group_id), + Some((_, group)) => { + let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); + row_rev.cells.insert(field_rev.id.clone(), cell_rev); + } + } + } +} + +pub struct MultiSelectGroupGenerator(); +impl GroupGenerator for MultiSelectGroupGenerator { + type ConfigurationType = SelectOptionGroupConfiguration; + type TypeOptionType = MultiSelectTypeOptionPB; + fn generate_groups( + field_id: &str, + _configuration: &Self::ConfigurationType, + type_option: &Option, + ) -> Vec { + match type_option { + None => vec![], + Some(type_option) => type_option + .options + .iter() + .map(|option| { + Group::new( + option.id.clone(), + field_id.to_owned(), + option.name.clone(), + option.id.clone(), + ) + }) + .collect(), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs new file mode 100644 index 0000000000..d48cdd8ee7 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -0,0 +1,100 @@ +use crate::entities::{GroupRowsChangesetPB, RowPB}; +use crate::services::cell::insert_select_option_cell; +use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB}; +use crate::services::group::action::GroupAction; + +use crate::services::group::controller::{ + GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, +}; +use crate::services::group::controller_impls::select_option_controller::util::*; +use crate::services::group::entities::Group; + +use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision}; + +// SingleSelect +pub type SingleSelectGroupController = GenericGroupController< + SelectOptionGroupConfigurationRevision, + SingleSelectTypeOptionPB, + SingleSelectGroupGenerator, + SelectOptionCellDataParser, +>; + +impl GroupAction for SingleSelectGroupController { + type CellDataType = SelectOptionCellDataPB; + fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { + cell_data.select_options.iter().any(|option| option.id == content) + } + + fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { + let mut changesets = vec![]; + self.configuration.with_mut_groups(|group| { + add_row(group, &mut changesets, cell_data, row_rev); + }); + changesets + } + + fn remove_row_if_match( + &mut self, + row_rev: &RowRevision, + cell_data: &Self::CellDataType, + ) -> Vec { + let mut changesets = vec![]; + self.configuration.with_mut_groups(|group| { + remove_row(group, &mut changesets, cell_data, row_rev); + }); + changesets + } + + fn move_row( + &mut self, + cell_data: &Self::CellDataType, + mut context: MoveGroupRowContext, + ) -> Vec { + let mut group_changeset = vec![]; + self.configuration.with_mut_groups(|group| { + move_select_option_row(group, &mut group_changeset, cell_data, &mut context); + }); + group_changeset + } +} + +impl GroupController for SingleSelectGroupController { + fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) { + let group: Option<&mut Group> = self.configuration.get_mut_group(group_id); + match group { + None => {} + Some(group) => { + let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); + row_rev.cells.insert(field_rev.id.clone(), cell_rev); + group.add_row(RowPB::from(row_rev)); + } + } + } +} + +pub struct SingleSelectGroupGenerator(); +impl GroupGenerator for SingleSelectGroupGenerator { + type ConfigurationType = SelectOptionGroupConfiguration; + type TypeOptionType = SingleSelectTypeOptionPB; + fn generate_groups( + field_id: &str, + _configuration: &Self::ConfigurationType, + type_option: &Option, + ) -> Vec { + match type_option { + None => vec![], + Some(type_option) => type_option + .options + .iter() + .map(|option| { + Group::new( + option.id.clone(), + field_id.to_owned(), + option.name.clone(), + option.id.clone(), + ) + }) + .collect(), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs new file mode 100644 index 0000000000..bdca688531 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs @@ -0,0 +1,105 @@ +use crate::entities::{GroupRowsChangesetPB, InsertedRowPB, RowPB}; +use crate::services::cell::insert_select_option_cell; +use crate::services::field::SelectOptionCellDataPB; +use crate::services::group::configuration::GenericGroupConfiguration; +use crate::services::group::Group; + +use crate::services::group::controller::MoveGroupRowContext; +use flowy_grid_data_model::revision::{RowRevision, SelectOptionGroupConfigurationRevision}; + +pub type SelectOptionGroupConfiguration = GenericGroupConfiguration; + +pub fn add_row( + group: &mut Group, + changesets: &mut Vec, + cell_data: &SelectOptionCellDataPB, + row_rev: &RowRevision, +) { + cell_data.select_options.iter().for_each(|option| { + if option.id == group.id { + if !group.contains_row(&row_rev.id) { + let row_pb = RowPB::from(row_rev); + changesets.push(GroupRowsChangesetPB::insert( + group.id.clone(), + vec![InsertedRowPB::new(row_pb.clone())], + )); + group.add_row(row_pb); + } + } else if group.contains_row(&row_rev.id) { + changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); + group.remove_row(&row_rev.id); + } + }); +} + +pub fn remove_row( + group: &mut Group, + changesets: &mut Vec, + cell_data: &SelectOptionCellDataPB, + row_rev: &RowRevision, +) { + cell_data.select_options.iter().for_each(|option| { + if option.id == group.id && group.contains_row(&row_rev.id) { + changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); + group.remove_row(&row_rev.id); + } + }); +} + +pub fn move_select_option_row( + group: &mut Group, + group_changeset: &mut Vec, + _cell_data: &SelectOptionCellDataPB, + context: &mut MoveGroupRowContext, +) { + let MoveGroupRowContext { + row_rev, + row_changeset, + field_rev, + to_group_id, + to_row_id, + } = context; + + let from_index = group.index_of_row(&row_rev.id); + let to_index = match to_row_id { + None => None, + Some(to_row_id) => group.index_of_row(to_row_id), + }; + + // Remove the row in which group contains it + if from_index.is_some() { + group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); + tracing::debug!("Group:{} remove row:{}", group.id, row_rev.id); + group.remove_row(&row_rev.id); + } + + if group.id == *to_group_id { + let row_pb = RowPB::from(*row_rev); + let mut inserted_row = InsertedRowPB::new(row_pb.clone()); + match to_index { + None => { + group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row])); + tracing::debug!("Group:{} append row:{}", group.id, row_rev.id); + group.add_row(row_pb); + } + Some(to_index) => { + if to_index < group.number_of_row() { + tracing::debug!("Group:{} insert row:{} at {} ", group.id, row_rev.id, to_index); + inserted_row.index = Some(to_index as i32); + group.insert_row(to_index, row_pb); + } else { + tracing::debug!("Group:{} append row:{}", group.id, row_rev.id); + group.add_row(row_pb); + } + group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row])); + } + } + + // Update the corresponding row's cell content. + if from_index.is_none() { + tracing::debug!("Mark row:{} belong to group:{}", row_rev.id, group.id); + let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); + row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev); + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/entities.rs b/frontend/rust-lib/flowy-grid/src/services/group/entities.rs new file mode 100644 index 0000000000..dd4171afb0 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/group/entities.rs @@ -0,0 +1,73 @@ +use crate::entities::{GroupPB, RowPB}; + +#[derive(Clone)] +pub struct Group { + pub id: String, + pub field_id: String, + pub desc: String, + rows: Vec, + + /// [content] is used to determine which group the cell belongs to. + pub content: String, +} + +impl std::convert::From for GroupPB { + fn from(group: Group) -> Self { + Self { + field_id: group.field_id, + group_id: group.id, + desc: group.desc, + rows: group.rows, + } + } +} + +impl Group { + pub fn new(id: String, field_id: String, desc: String, content: String) -> Self { + Self { + id, + field_id, + desc, + rows: vec![], + content, + } + } + + pub fn contains_row(&self, row_id: &str) -> bool { + self.rows.iter().any(|row| row.id == row_id) + } + + pub fn remove_row(&mut self, row_id: &str) { + match self.rows.iter().position(|row| row.id == row_id) { + None => {} + Some(pos) => { + self.rows.remove(pos); + } + } + } + + pub fn add_row(&mut self, row_pb: RowPB) { + match self.rows.iter().find(|row| row.id == row_pb.id) { + None => { + self.rows.push(row_pb); + } + Some(_) => {} + } + } + + pub fn insert_row(&mut self, index: usize, row_pb: RowPB) { + if index < self.rows.len() { + self.rows.insert(index, row_pb); + } else { + tracing::error!("Insert row index:{} beyond the bounds:{},", index, self.rows.len()); + } + } + + pub fn index_of_row(&self, row_id: &str) -> Option { + self.rows.iter().position(|row| row.id == row_id) + } + + pub fn number_of_row(&self) -> usize { + self.rows.len() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs deleted file mode 100644 index a1d5df2154..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs +++ /dev/null @@ -1,296 +0,0 @@ -use crate::entities::{GroupPB, GroupRowsChangesetPB, RowPB}; -use crate::services::cell::{decode_any_cell_data, CellBytesParser}; -use bytes::Bytes; -use flowy_error::FlowyResult; -use flowy_grid_data_model::revision::{ - FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer, -}; -use indexmap::IndexMap; -use std::marker::PhantomData; -use std::sync::Arc; - -pub trait GroupGenerator { - type ConfigurationType; - type TypeOptionType; - - fn generate_groups( - field_id: &str, - configuration: &Option, - type_option: &Option, - ) -> Vec; -} - -pub trait Groupable: Send + Sync { - type CellDataType; - fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; - fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; - fn remove_row_if_match( - &mut self, - row_rev: &RowRevision, - cell_data: &Self::CellDataType, - ) -> Vec; - - fn move_row_if_match( - &mut self, - field_rev: &FieldRevision, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - cell_data: &Self::CellDataType, - to_row_id: &str, - ) -> Vec; -} - -pub trait GroupController: GroupControllerSharedAction + Send + Sync { - fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str); -} - -pub trait GroupControllerSharedAction: Send + Sync { - // The field that is used for grouping the rows - fn field_id(&self) -> &str; - fn groups(&self) -> Vec; - fn group_rows(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult<()>; - fn did_update_row( - &mut self, - row_rev: &RowRevision, - field_rev: &FieldRevision, - ) -> FlowyResult>; - - fn did_delete_row( - &mut self, - row_rev: &RowRevision, - field_rev: &FieldRevision, - ) -> FlowyResult>; - - fn did_move_row( - &mut self, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - field_rev: &FieldRevision, - to_row_id: &str, - ) -> FlowyResult>; -} - -const DEFAULT_GROUP_ID: &str = "default_group"; - -/// C: represents the group configuration structure -/// T: the type option data deserializer that impl [TypeOptionDataDeserializer] -/// G: the group generator, [GroupGenerator] -/// P: the parser that impl [CellBytesParser] for the CellBytes -pub struct GenericGroupController { - pub field_id: String, - pub groups_map: IndexMap, - default_group: Group, - pub type_option: Option, - pub configuration: Option, - group_action_phantom: PhantomData, - cell_parser_phantom: PhantomData

, -} - -#[derive(Clone)] -pub struct Group { - pub id: String, - pub field_id: String, - pub desc: String, - rows: Vec, - pub content: String, -} - -impl std::convert::From for GroupPB { - fn from(group: Group) -> Self { - Self { - field_id: group.field_id, - group_id: group.id, - desc: group.desc, - rows: group.rows, - } - } -} - -impl Group { - pub fn new(id: String, field_id: String, desc: String, content: String) -> Self { - Self { - id, - field_id, - desc, - rows: vec![], - content, - } - } - - pub fn contains_row(&self, row_id: &str) -> bool { - self.rows.iter().any(|row| row.id == row_id) - } - - pub fn remove_row(&mut self, row_id: &str) { - match self.rows.iter().position(|row| row.id == row_id) { - None => {} - Some(pos) => { - self.rows.remove(pos); - } - } - } - - pub fn add_row(&mut self, row_pb: RowPB) { - match self.rows.iter().find(|row| row.id == row_pb.id) { - None => { - self.rows.push(row_pb); - } - Some(_) => {} - } - } - - pub fn insert_row(&mut self, index: usize, row_pb: RowPB) { - if index < self.rows.len() { - self.rows.insert(index, row_pb); - } else { - tracing::error!("Insert row index:{} beyond the bounds:{},", index, self.rows.len()); - } - } - - pub fn index_of_row(&self, row_id: &str) -> Option { - self.rows.iter().position(|row| row.id == row_id) - } - - pub fn number_of_row(&self) -> usize { - self.rows.len() - } -} - -impl GenericGroupController -where - C: TryFrom, - T: TypeOptionDataDeserializer, - G: GroupGenerator, -{ - pub fn new(field_rev: &Arc, configuration: GroupConfigurationRevision) -> FlowyResult { - let configuration = match configuration.content { - None => None, - Some(content) => Some(C::try_from(Bytes::from(content))?), - }; - let field_type_rev = field_rev.ty; - let type_option = field_rev.get_type_option_entry::(field_type_rev); - let groups = G::generate_groups(&field_rev.id, &configuration, &type_option); - - let default_group = Group::new( - DEFAULT_GROUP_ID.to_owned(), - field_rev.id.clone(), - format!("No {}", field_rev.name), - "".to_string(), - ); - - Ok(Self { - field_id: field_rev.id.clone(), - groups_map: groups.into_iter().map(|group| (group.id.clone(), group)).collect(), - default_group, - type_option, - configuration, - group_action_phantom: PhantomData, - cell_parser_phantom: PhantomData, - }) - } -} - -impl GroupControllerSharedAction for GenericGroupController -where - P: CellBytesParser, - Self: Groupable, -{ - fn field_id(&self) -> &str { - &self.field_id - } - - fn groups(&self) -> Vec { - let default_group = self.default_group.clone(); - let mut groups: Vec = self.groups_map.values().cloned().collect(); - if !default_group.rows.is_empty() { - groups.push(default_group); - } - groups - } - - fn group_rows(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult<()> { - if self.configuration.is_none() { - return Ok(()); - } - - for row_rev in row_revs { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let mut records: Vec = vec![]; - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); - let cell_data = cell_bytes.parser::

()?; - for group in self.groups_map.values() { - if self.can_group(&group.content, &cell_data) { - records.push(GroupRecord { - row: row_rev.into(), - group_id: group.id.clone(), - }); - } - } - - if records.is_empty() { - self.default_group.rows.push(row_rev.into()); - } else { - for record in records { - if let Some(group) = self.groups_map.get_mut(&record.group_id) { - group.rows.push(record.row); - } - } - } - } else { - self.default_group.rows.push(row_rev.into()); - } - } - - Ok(()) - } - - fn did_update_row( - &mut self, - row_rev: &RowRevision, - field_rev: &FieldRevision, - ) -> FlowyResult> { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); - let cell_data = cell_bytes.parser::

()?; - Ok(self.add_row_if_match(row_rev, &cell_data)) - } else { - Ok(vec![]) - } - } - - fn did_delete_row( - &mut self, - row_rev: &RowRevision, - field_rev: &FieldRevision, - ) -> FlowyResult> { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); - let cell_data = cell_bytes.parser::

()?; - Ok(self.remove_row_if_match(row_rev, &cell_data)) - } else { - Ok(vec![]) - } - } - - fn did_move_row( - &mut self, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - field_rev: &FieldRevision, - to_row_id: &str, - ) -> FlowyResult> { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); - let cell_data = cell_bytes.parser::

()?; - tracing::trace!("Move row:{} to row:{}", row_rev.id, to_row_id); - Ok(self.move_row_if_match(field_rev, row_rev, row_changeset, &cell_data, to_row_id)) - } else { - Ok(vec![]) - } - } -} - -struct GroupRecord { - row: RowPB, - group_id: String, -} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/mod.rs deleted file mode 100644 index 08e691a75e..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod checkbox_group; -mod group_controller; -mod select_option_group; - -pub use checkbox_group::*; -pub use group_controller::*; -pub use select_option_group::*; 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 deleted file mode 100644 index 6e569a7b54..0000000000 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs +++ /dev/null @@ -1,287 +0,0 @@ -use crate::entities::{GroupRowsChangesetPB, InsertedRowPB, RowPB, SelectOptionGroupConfigurationPB}; -use crate::services::cell::insert_select_option_cell; -use crate::services::field::{ - MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB, -}; -use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable}; - -use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; - -// SingleSelect -pub type SingleSelectGroupController = GenericGroupController< - SelectOptionGroupConfigurationPB, - SingleSelectTypeOptionPB, - SingleSelectGroupGenerator, - SelectOptionCellDataParser, ->; - -impl Groupable for SingleSelectGroupController { - type CellDataType = SelectOptionCellDataPB; - fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { - cell_data.select_options.iter().any(|option| option.id == content) - } - - fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { - let mut changesets = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - add_row(group, &mut changesets, cell_data, row_rev); - }); - changesets - } - - fn remove_row_if_match( - &mut self, - row_rev: &RowRevision, - cell_data: &Self::CellDataType, - ) -> Vec { - let mut changesets = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - remove_row(group, &mut changesets, cell_data, row_rev); - }); - changesets - } - - fn move_row_if_match( - &mut self, - field_rev: &FieldRevision, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - cell_data: &Self::CellDataType, - to_row_id: &str, - ) -> Vec { - let mut group_changeset = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - move_row( - group, - &mut group_changeset, - field_rev, - row_rev, - row_changeset, - cell_data, - to_row_id, - ); - }); - group_changeset - } -} - -impl GroupController for SingleSelectGroupController { - fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) { - let group: Option<&mut Group> = self.groups_map.get_mut(group_id); - match group { - None => {} - Some(group) => { - let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); - row_rev.cells.insert(field_rev.id.clone(), cell_rev); - group.add_row(RowPB::from(row_rev)); - } - } - } -} - -pub struct SingleSelectGroupGenerator(); -impl GroupGenerator for SingleSelectGroupGenerator { - type ConfigurationType = SelectOptionGroupConfigurationPB; - type TypeOptionType = SingleSelectTypeOptionPB; - fn generate_groups( - field_id: &str, - _configuration: &Option, - type_option: &Option, - ) -> Vec { - match type_option { - None => vec![], - Some(type_option) => type_option - .options - .iter() - .map(|option| { - Group::new( - option.id.clone(), - field_id.to_owned(), - option.name.clone(), - option.id.clone(), - ) - }) - .collect(), - } - } -} - -// MultiSelect -pub type MultiSelectGroupController = GenericGroupController< - SelectOptionGroupConfigurationPB, - MultiSelectTypeOptionPB, - MultiSelectGroupGenerator, - SelectOptionCellDataParser, ->; - -impl Groupable for MultiSelectGroupController { - type CellDataType = SelectOptionCellDataPB; - - fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { - cell_data.select_options.iter().any(|option| option.id == content) - } - - fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { - let mut changesets = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - add_row(group, &mut changesets, cell_data, row_rev); - }); - changesets - } - - fn remove_row_if_match( - &mut self, - row_rev: &RowRevision, - cell_data: &Self::CellDataType, - ) -> Vec { - let mut changesets = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - remove_row(group, &mut changesets, cell_data, row_rev); - }); - changesets - } - - fn move_row_if_match( - &mut self, - field_rev: &FieldRevision, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - cell_data: &Self::CellDataType, - to_row_id: &str, - ) -> Vec { - let mut group_changeset = vec![]; - self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - move_row( - group, - &mut group_changeset, - field_rev, - row_rev, - row_changeset, - cell_data, - to_row_id, - ); - }); - group_changeset - } -} - -impl GroupController for MultiSelectGroupController { - fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) { - let group: Option<&Group> = self.groups_map.get(group_id); - match group { - None => tracing::warn!("Can not find the group: {}", group_id), - Some(group) => { - let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); - row_rev.cells.insert(field_rev.id.clone(), cell_rev); - } - } - } -} - -pub struct MultiSelectGroupGenerator(); -impl GroupGenerator for MultiSelectGroupGenerator { - type ConfigurationType = SelectOptionGroupConfigurationPB; - type TypeOptionType = MultiSelectTypeOptionPB; - - fn generate_groups( - field_id: &str, - _configuration: &Option, - type_option: &Option, - ) -> Vec { - match type_option { - None => vec![], - Some(type_option) => type_option - .options - .iter() - .map(|option| { - Group::new( - option.id.clone(), - field_id.to_owned(), - option.name.clone(), - option.id.clone(), - ) - }) - .collect(), - } - } -} - -fn add_row( - group: &mut Group, - changesets: &mut Vec, - cell_data: &SelectOptionCellDataPB, - row_rev: &RowRevision, -) { - cell_data.select_options.iter().for_each(|option| { - if option.id == group.id { - if !group.contains_row(&row_rev.id) { - let row_pb = RowPB::from(row_rev); - changesets.push(GroupRowsChangesetPB::insert( - group.id.clone(), - vec![InsertedRowPB::new(row_pb.clone())], - )); - group.add_row(row_pb); - } - } else if group.contains_row(&row_rev.id) { - changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); - group.remove_row(&row_rev.id); - } - }); -} - -fn remove_row( - group: &mut Group, - changesets: &mut Vec, - cell_data: &SelectOptionCellDataPB, - row_rev: &RowRevision, -) { - cell_data.select_options.iter().for_each(|option| { - if option.id == group.id && group.contains_row(&row_rev.id) { - changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); - group.remove_row(&row_rev.id); - } - }); -} - -fn move_row( - group: &mut Group, - group_changeset: &mut Vec, - field_rev: &FieldRevision, - row_rev: &RowRevision, - row_changeset: &mut RowChangeset, - cell_data: &SelectOptionCellDataPB, - to_row_id: &str, -) { - cell_data.select_options.iter().for_each(|option| { - // Remove the row in which group contains the row - let is_group_contains = group.contains_row(&row_rev.id); - let to_index = group.index_of_row(to_row_id); - - if option.id == group.id && is_group_contains { - group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); - group.remove_row(&row_rev.id); - } - - // Find the inserted group - if let Some(to_index) = to_index { - let row_pb = RowPB::from(row_rev); - let inserted_row = InsertedRowPB { - row: row_pb.clone(), - index: Some(to_index as i32), - }; - group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row])); - if group.number_of_row() == to_index { - group.add_row(row_pb); - } else { - group.insert_row(to_index, row_pb); - } - } - - // If the inserted row comes from other group, it needs to update the corresponding cell content. - if to_index.is_some() && option.id != group.id { - // Update the corresponding row's cell content. - let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); - row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev); - } - }); -} 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 0e152bf27c..6afec9dd82 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,43 +1,49 @@ -use crate::entities::{ - CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupRowsChangesetPB, - NumberGroupConfigurationPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB, -}; +use crate::entities::{FieldType, GroupRowsChangesetPB}; +use crate::services::group::configuration::GroupConfigurationReader; +use crate::services::group::controller::{GroupController, MoveGroupRowContext}; use crate::services::group::{ - CheckboxGroupController, Group, GroupController, MultiSelectGroupController, SingleSelectGroupController, + CheckboxGroupConfiguration, CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController, + SelectOptionGroupConfiguration, SingleSelectGroupController, }; -use bytes::Bytes; use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{ - gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision, + CheckboxGroupConfigurationRevision, DateGroupConfigurationRevision, FieldRevision, GroupConfigurationRevision, + NumberGroupConfigurationRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision, + TextGroupConfigurationRevision, UrlGroupConfigurationRevision, }; -use lib_infra::future::AFFuture; use std::future::Future; use std::sync::Arc; -use tokio::sync::RwLock; - -pub trait GroupConfigurationDelegate: Send + Sync + 'static { - fn get_group_configuration(&self, field_rev: Arc) -> AFFuture; -} pub(crate) struct GroupService { - delegate: Box, - group_controller: Option>>, + configuration_reader: Arc, + configuration_writer: Arc, + group_controller: Option>, } impl GroupService { - pub(crate) async fn new(delegate: Box) -> Self { + pub(crate) async fn new(configuration_reader: R, configuration_writer: W) -> Self + where + R: GroupConfigurationReader, + W: GroupConfigurationWriter, + { Self { - delegate, + configuration_reader: Arc::new(configuration_reader), + configuration_writer: Arc::new(configuration_writer), group_controller: None, } } pub(crate) async fn groups(&self) -> Vec { - if let Some(group_action_handler) = self.group_controller.as_ref() { - group_action_handler.read().await.groups() - } else { - vec![] - } + self.group_controller + .as_ref() + .and_then(|group_controller| Some(group_controller.groups())) + .unwrap_or(vec![]) + } + + pub(crate) async fn get_group(&self, group_id: &str) -> Option<(usize, Group)> { + self.group_controller + .as_ref() + .and_then(|group_controller| group_controller.get_group(group_id)) } pub(crate) async fn load_groups( @@ -47,37 +53,37 @@ impl GroupService { ) -> Option> { let field_rev = find_group_field(field_revs)?; let field_type: FieldType = field_rev.ty.into(); - let configuration = self.delegate.get_group_configuration(field_rev.clone()).await; - match self - .build_groups(&field_type, &field_rev, row_revs, configuration) - .await - { - Ok(groups) => Some(groups), - Err(_) => None, - } + + let mut group_controller = self.make_group_controller(&field_type, &field_rev).await.ok()??; + let groups = match group_controller.fill_groups(&row_revs, &field_rev) { + Ok(groups) => groups, + Err(e) => { + tracing::error!("Fill groups failed:{:?}", e); + vec![] + } + }; + self.group_controller = Some(group_controller); + Some(groups) } - pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F) + pub(crate) async fn will_create_row(&mut self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F) where F: FnOnce(String) -> O, O: Future>> + Send + Sync + 'static, { - if let Some(group_controller) = self.group_controller.as_ref() { - let field_id = group_controller.read().await.field_id().to_owned(); + if let Some(group_controller) = self.group_controller.as_mut() { + let field_id = group_controller.field_id().to_owned(); match get_field_fn(field_id).await { None => {} Some(field_rev) => { - group_controller - .write() - .await - .will_create_row(row_rev, &field_rev, group_id); + group_controller.will_create_row(row_rev, &field_rev, group_id); } } } } pub(crate) async fn did_delete_row( - &self, + &mut self, row_rev: &RowRevision, get_field_fn: F, ) -> Option> @@ -85,11 +91,11 @@ impl GroupService { F: FnOnce(String) -> O, O: Future>> + Send + Sync + 'static, { - let group_controller = self.group_controller.as_ref()?; - let field_id = group_controller.read().await.field_id().to_owned(); + let group_controller = self.group_controller.as_mut()?; + let field_id = group_controller.field_id().to_owned(); let field_rev = get_field_fn(field_id).await?; - match group_controller.write().await.did_delete_row(row_rev, &field_rev) { + match group_controller.did_delete_row(row_rev, &field_rev) { Ok(changesets) => Some(changesets), Err(e) => { tracing::error!("Delete group data failed, {:?}", e); @@ -98,26 +104,30 @@ impl GroupService { } } - pub(crate) async fn did_move_row( - &self, + pub(crate) async fn move_group_row( + &mut self, row_rev: &RowRevision, row_changeset: &mut RowChangeset, - upper_row_id: &str, + to_group_id: &str, + to_row_id: Option, get_field_fn: F, ) -> Option> where F: FnOnce(String) -> O, O: Future>> + Send + Sync + 'static, { - let group_controller = self.group_controller.as_ref()?; - let field_id = group_controller.read().await.field_id().to_owned(); + let group_controller = self.group_controller.as_mut()?; + let field_id = group_controller.field_id().to_owned(); let field_rev = get_field_fn(field_id).await?; + let move_row_context = MoveGroupRowContext { + row_rev, + row_changeset, + field_rev: field_rev.as_ref(), + to_group_id, + to_row_id, + }; - match group_controller - .write() - .await - .did_move_row(row_rev, row_changeset, &field_rev, upper_row_id) - { + match group_controller.move_group_row(move_row_context) { Ok(changesets) => Some(changesets), Err(e) => { tracing::error!("Move group data failed, {:?}", e); @@ -128,7 +138,7 @@ impl GroupService { #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn did_update_row( - &self, + &mut self, row_rev: &RowRevision, get_field_fn: F, ) -> Option> @@ -136,11 +146,11 @@ impl GroupService { F: FnOnce(String) -> O, O: Future>> + Send + Sync + 'static, { - let group_controller = self.group_controller.as_ref()?; - let field_id = group_controller.read().await.field_id().to_owned(); + let group_controller = self.group_controller.as_mut()?; + let field_id = group_controller.field_id().to_owned(); let field_rev = get_field_fn(field_id).await?; - match group_controller.write().await.did_update_row(row_rev, &field_rev) { + match group_controller.did_update_row(row_rev, &field_rev) { Ok(changeset) => Some(changeset), Err(e) => { tracing::error!("Update group data failed, {:?}", e); @@ -149,14 +159,24 @@ impl GroupService { } } - #[tracing::instrument(level = "trace", skip_all, err)] - async fn build_groups( - &mut self, + #[tracing::instrument(level = "trace", skip_all)] + pub(crate) async fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> { + match self.group_controller.as_mut() { + None => Ok(()), + Some(group_controller) => { + let _ = group_controller.move_group(from_group_id, to_group_id)?; + Ok(()) + } + } + } + + #[tracing::instrument(level = "trace", skip(self, field_rev), err)] + async fn make_group_controller( + &self, field_type: &FieldType, field_rev: &Arc, - row_revs: Vec>, - configuration: GroupConfigurationRevision, - ) -> FlowyResult> { + ) -> FlowyResult>> { + let mut group_controller: Option> = None; match field_type { FieldType::RichText => { // let generator = GroupGenerator::::from_configuration(configuration); @@ -168,31 +188,40 @@ impl GroupService { // let generator = GroupGenerator::::from_configuration(configuration); } FieldType::SingleSelect => { - let controller = SingleSelectGroupController::new(field_rev, configuration)?; - self.group_controller = Some(Arc::new(RwLock::new(controller))); + let configuration = SelectOptionGroupConfiguration::new( + field_rev.clone(), + self.configuration_reader.clone(), + self.configuration_writer.clone(), + ) + .await?; + let controller = SingleSelectGroupController::new(field_rev, configuration).await?; + group_controller = Some(Box::new(controller)); } FieldType::MultiSelect => { - let controller = MultiSelectGroupController::new(field_rev, configuration)?; - self.group_controller = Some(Arc::new(RwLock::new(controller))); + let configuration = SelectOptionGroupConfiguration::new( + field_rev.clone(), + self.configuration_reader.clone(), + self.configuration_writer.clone(), + ) + .await?; + let controller = MultiSelectGroupController::new(field_rev, configuration).await?; + group_controller = Some(Box::new(controller)); } FieldType::Checkbox => { - let controller = CheckboxGroupController::new(field_rev, configuration)?; - self.group_controller = Some(Arc::new(RwLock::new(controller))); + let configuration = CheckboxGroupConfiguration::new( + field_rev.clone(), + self.configuration_reader.clone(), + self.configuration_writer.clone(), + ) + .await?; + let controller = CheckboxGroupController::new(field_rev, configuration).await?; + group_controller = Some(Box::new(controller)); } FieldType::URL => { // let generator = GroupGenerator::::from_configuration(configuration); } - }; - - let mut groups = vec![]; - if let Some(group_action_handler) = self.group_controller.as_ref() { - let mut write_guard = group_action_handler.write().await; - let _ = write_guard.group_rows(&row_revs, field_rev)?; - groups = write_guard.groups(); - drop(write_guard); } - - Ok(groups) + Ok(group_controller) } } @@ -208,20 +237,41 @@ fn find_group_field(field_revs: &[Arc]) -> Option GroupConfigurationRevision { + let field_id = field_rev.id.clone(); + let field_type_rev = field_rev.ty; let field_type: FieldType = field_rev.ty.into(); - let bytes: Bytes = match field_type { - FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(), - FieldType::Number => NumberGroupConfigurationPB::default().try_into().unwrap(), - FieldType::DateTime => DateGroupConfigurationPB::default().try_into().unwrap(), - FieldType::SingleSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(), - FieldType::MultiSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(), - FieldType::Checkbox => CheckboxGroupConfigurationPB::default().try_into().unwrap(), - FieldType::URL => UrlGroupConfigurationPB::default().try_into().unwrap(), - }; - GroupConfigurationRevision { - id: gen_grid_group_id(), - field_id: field_rev.id.clone(), - field_type_rev: field_rev.ty, - content: Some(bytes.to_vec()), + match field_type { + FieldType::RichText => { + GroupConfigurationRevision::new(field_id, field_type_rev, TextGroupConfigurationRevision::default()) + .unwrap() + } + FieldType::Number => { + GroupConfigurationRevision::new(field_id, field_type_rev, NumberGroupConfigurationRevision::default()) + .unwrap() + } + FieldType::DateTime => { + GroupConfigurationRevision::new(field_id, field_type_rev, DateGroupConfigurationRevision::default()) + .unwrap() + } + + FieldType::SingleSelect => GroupConfigurationRevision::new( + field_id, + field_type_rev, + SelectOptionGroupConfigurationRevision::default(), + ) + .unwrap(), + FieldType::MultiSelect => GroupConfigurationRevision::new( + field_id, + field_type_rev, + SelectOptionGroupConfigurationRevision::default(), + ) + .unwrap(), + FieldType::Checkbox => { + GroupConfigurationRevision::new(field_id, field_type_rev, CheckboxGroupConfigurationRevision::default()) + .unwrap() + } + FieldType::URL => { + GroupConfigurationRevision::new(field_id, field_type_rev, UrlGroupConfigurationRevision::default()).unwrap() + } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/mod.rs index 15b401f4c0..2bc979c28d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/mod.rs @@ -1,5 +1,11 @@ -mod group_generator; +mod action; +mod configuration; +mod controller; +mod controller_impls; +mod entities; mod group_service; -pub(crate) use group_generator::*; +pub(crate) use configuration::*; +pub(crate) use controller_impls::*; +pub(crate) use entities::*; pub(crate) use group_service::*; 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 7d285fbe2b..16ee630cc7 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,11 +1,4 @@ -use crate::entities::{ - GridLayout, GridLayoutPB, GridSettingPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, - RepeatedGridSortPB, -}; -use flowy_grid_data_model::revision::{FieldRevision, SettingRevision}; -use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams}; -use std::collections::HashMap; -use std::sync::Arc; +use crate::entities::{CreateFilterParams, DeleteFilterParams, GridLayout, GridSettingChangesetParams}; pub struct GridSettingChangesetBuilder { params: GridSettingChangesetParams, @@ -20,13 +13,11 @@ impl GridSettingChangesetBuilder { delete_filter: None, insert_group: None, delete_group: None, - insert_sort: None, - delete_sort: None, }; Self { params } } - pub fn insert_filter(mut self, params: CreateGridFilterParams) -> Self { + pub fn insert_filter(mut self, params: CreateFilterParams) -> Self { self.params.insert_filter = Some(params); self } @@ -40,42 +31,3 @@ impl GridSettingChangesetBuilder { self.params } } - -pub fn make_grid_setting(grid_setting_rev: &SettingRevision, field_revs: &[Arc]) -> GridSettingPB { - 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| { - filters_by_field_id - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect::>() - }) - .unwrap_or_default(); - let groups_by_field_id = grid_setting_rev - .get_all_groups(field_revs) - .map(|groups_by_field_id| { - groups_by_field_id - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect::>() - }) - .unwrap_or_default(); - let sorts_by_field_id = grid_setting_rev - .get_all_sort() - .map(|sorts_by_field_id| { - sorts_by_field_id - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect::>() - }) - .unwrap_or_default(); - - GridSettingPB { - layouts: GridLayoutPB::all(), - current_layout_type, - filter_configuration_by_field_id: filters_by_field_id, - group_configuration_by_field_id: groups_by_field_id, - sorts_by_field_id, - } -} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs index ebe3d3adf2..c790285fb1 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs @@ -1,7 +1,6 @@ use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::InsertFieldParams; +use flowy_grid::entities::{FieldChangesetParams, InsertFieldParams}; use flowy_grid_data_model::revision::FieldRevision; -use flowy_sync::entities::grid::FieldChangesetParams; pub enum FieldScript { CreateField { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs index 98cb3d0324..3e255d3662 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs @@ -1,10 +1,10 @@ use crate::grid::field_test::script::FieldScript::*; use crate::grid::field_test::script::GridFieldTest; use crate::grid::field_test::util::*; +use flowy_grid::entities::FieldChangesetParams; use flowy_grid::services::field::selection_type_option::SelectOptionPB; use flowy_grid::services::field::SingleSelectTypeOptionPB; use flowy_grid_data_model::revision::TypeOptionDataEntry; -use flowy_sync::entities::grid::FieldChangesetParams; #[tokio::test] async fn grid_create_field() { 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 650564d8d5..a1f0b8a0c6 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,17 +3,12 @@ #![allow(dead_code)] #![allow(unused_imports)] -use flowy_grid::entities::{CreateGridFilterPayloadPB, GridLayout, GridSettingPB}; +use flowy_grid::entities::{CreateFilterParams, CreateGridFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB}; use flowy_grid::services::setting::GridSettingChangesetBuilder; use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision}; -use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams}; use crate::grid::grid_editor::GridEditorTest; pub enum FilterScript { - #[allow(dead_code)] - UpdateGridSetting { - params: GridSettingChangesetParams, - }, InsertGridTableFilter { payload: CreateGridFilterPayloadPB, }, @@ -50,27 +45,18 @@ impl GridFilterTest { pub async fn run_script(&mut self, script: FilterScript) { match script { - FilterScript::UpdateGridSetting { params } => { - let _ = self.editor.update_grid_setting(params).await.unwrap(); - } + FilterScript::InsertGridTableFilter { payload } => { - let params: CreateGridFilterParams = payload.try_into().unwrap(); - let layout_type = GridLayout::Table; - let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) - .insert_filter(params) - .build(); - let _ = self.editor.update_grid_setting(params).await.unwrap(); + let params: CreateFilterParams = payload.try_into().unwrap(); + let _ = self.editor.update_filter(params).await.unwrap(); } FilterScript::AssertTableFilterCount { count } => { let filters = self.editor.get_grid_filter().await.unwrap(); assert_eq!(count as usize, filters.len()); } FilterScript::DeleteGridTableFilter { filter_id, field_rev} => { - 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.ty }) - .build(); - let _ = self.editor.update_grid_setting(params).await.unwrap(); + let params = DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.ty }; + let _ = self.editor.delete_filter(params).await.unwrap(); } FilterScript::AssertGridSetting { expected_setting } => { let setting = self.editor.get_grid_setting().await.unwrap(); diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index dbd5df1484..78cfc17b26 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -12,9 +12,6 @@ use flowy_grid::services::setting::GridSettingChangesetBuilder; use flowy_grid_data_model::revision::*; use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS; use flowy_sync::client_grid::GridBuilder; -use flowy_sync::entities::grid::{ - CreateGridFilterParams, DeleteFilterParams, FieldChangesetParams, GridSettingChangesetParams, -}; use flowy_test::helper::ViewTest; use flowy_test::FlowySDKTest; use std::collections::HashMap; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs index 1900e89bff..94ef4dff1b 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs @@ -1,14 +1,20 @@ use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::{CreateRowParams, FieldType, GridLayout, GroupPB, MoveRowParams, RowPB}; +use flowy_grid::entities::{ + CreateRowParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB, +}; use flowy_grid::services::cell::insert_select_option_cell; use flowy_grid_data_model::revision::RowChangeset; pub enum GroupScript { - AssertGroup { + AssertGroupRowCount { group_index: usize, row_count: usize, }, AssertGroupCount(usize), + AssertGroup { + group_index: usize, + expected_group: GroupPB, + }, AssertRow { group_index: usize, row_index: usize, @@ -32,6 +38,10 @@ pub enum GroupScript { row_index: usize, to_group_index: usize, }, + MoveGroup { + from_group_index: usize, + to_group_index: usize, + }, } pub struct GridGroupTest { @@ -52,7 +62,7 @@ impl GridGroupTest { pub async fn run_script(&mut self, script: GroupScript) { match script { - GroupScript::AssertGroup { group_index, row_count } => { + GroupScript::AssertGroupRowCount { group_index, row_count } => { assert_eq!(row_count, self.group_at_index(group_index).await.rows.len()); } GroupScript::AssertGroupCount(count) => { @@ -67,14 +77,16 @@ impl GridGroupTest { } => { let groups: Vec = self.editor.load_groups().await.unwrap().items; let from_row = groups.get(from_group_index).unwrap().rows.get(from_row_index).unwrap(); - let to_row = groups.get(to_group_index).unwrap().rows.get(to_row_index).unwrap(); - let params = MoveRowParams { + let to_group = groups.get(to_group_index).unwrap(); + let to_row = to_group.rows.get(to_row_index).unwrap(); + let params = MoveGroupRowParams { view_id: self.inner.grid_id.clone(), from_row_id: from_row.id.clone(), - to_row_id: to_row.id.clone(), + to_group_id: to_group.group_id.clone(), + to_row_id: Some(to_row.id.clone()), }; - self.editor.move_row(params).await.unwrap(); + self.editor.move_group_row(params).await.unwrap(); } GroupScript::AssertRow { group_index, @@ -84,7 +96,6 @@ impl GridGroupTest { // let group = self.group_at_index(group_index).await; let compare_row = group.rows.get(row_index).unwrap().clone(); - assert_eq!(row.id, compare_row.id); } GroupScript::CreateRow { group_index } => { @@ -125,6 +136,27 @@ impl GridGroupTest { row_changeset.cell_by_field_id.insert(field_id, cell_rev); self.editor.update_row(row_changeset).await.unwrap(); } + GroupScript::MoveGroup { + from_group_index, + to_group_index, + } => { + let from_group = self.group_at_index(from_group_index).await; + let to_group = self.group_at_index(to_group_index).await; + let params = MoveGroupParams { + view_id: self.editor.grid_id.clone(), + from_group_id: from_group.group_id, + to_group_id: to_group.group_id, + }; + self.editor.move_group(params).await.unwrap(); + // + } + GroupScript::AssertGroup { + group_index, + expected_group: group_pb, + } => { + let group = self.group_at_index(group_index).await; + assert_eq!(group.group_id, group_pb.group_id); + } } } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs index 798f478439..bebf499773 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs @@ -2,19 +2,19 @@ use crate::grid::group_test::script::GridGroupTest; use crate::grid::group_test::script::GroupScript::*; #[tokio::test] -async fn board_init_test() { +async fn group_init_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ AssertGroupCount(3), - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 2, }, - AssertGroup { + AssertGroupRowCount { group_index: 1, row_count: 2, }, - AssertGroup { + AssertGroupRowCount { group_index: 2, row_count: 1, }, @@ -23,7 +23,7 @@ async fn board_init_test() { } #[tokio::test] -async fn board_move_row_test() { +async fn group_move_row_test() { let mut test = GridGroupTest::new().await; let group = test.group_at_index(0).await; let scripts = vec![ @@ -34,7 +34,7 @@ async fn board_move_row_test() { to_group_index: 0, to_row_index: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 2, }, @@ -48,7 +48,7 @@ async fn board_move_row_test() { } #[tokio::test] -async fn board_move_row_to_other_group_test() { +async fn group_move_row_to_other_group_test() { let mut test = GridGroupTest::new().await; let group = test.group_at_index(0).await; let scripts = vec![ @@ -58,11 +58,11 @@ async fn board_move_row_to_other_group_test() { to_group_index: 1, to_row_index: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 1, row_count: 3, }, @@ -76,7 +76,7 @@ async fn board_move_row_to_other_group_test() { } #[tokio::test] -async fn board_move_row_to_other_group_and_reorder_test() { +async fn group_move_two_row_to_other_group_test() { let mut test = GridGroupTest::new().await; let group = test.group_at_index(0).await; let scripts = vec![ @@ -86,15 +86,41 @@ async fn board_move_row_to_other_group_and_reorder_test() { to_group_index: 1, to_row_index: 1, }, - MoveRow { - from_group_index: 1, - from_row_index: 1, - to_group_index: 1, - to_row_index: 2, + AssertGroupRowCount { + group_index: 0, + row_count: 1, + }, + AssertGroupRowCount { + group_index: 1, + row_count: 3, }, AssertRow { group_index: 1, - row_index: 2, + row_index: 1, + row: group.rows.get(0).unwrap().clone(), + }, + ]; + test.run_scripts(scripts).await; + + let group = test.group_at_index(0).await; + let scripts = vec![ + MoveRow { + from_group_index: 0, + from_row_index: 0, + to_group_index: 1, + to_row_index: 1, + }, + AssertGroupRowCount { + group_index: 0, + row_count: 0, + }, + AssertGroupRowCount { + group_index: 1, + row_count: 4, + }, + AssertRow { + group_index: 1, + row_index: 1, row: group.rows.get(0).unwrap().clone(), }, ]; @@ -102,17 +128,84 @@ async fn board_move_row_to_other_group_and_reorder_test() { } #[tokio::test] -async fn board_create_row_test() { +async fn group_move_row_to_other_group_and_reorder_from_up_to_down_test() { + let mut test = GridGroupTest::new().await; + let group_0 = test.group_at_index(0).await; + let group_1 = test.group_at_index(1).await; + let scripts = vec![ + MoveRow { + from_group_index: 0, + from_row_index: 0, + to_group_index: 1, + to_row_index: 1, + }, + AssertRow { + group_index: 1, + row_index: 1, + row: group_0.rows.get(0).unwrap().clone(), + }, + ]; + test.run_scripts(scripts).await; + + let scripts = vec![ + MoveRow { + from_group_index: 1, + from_row_index: 0, + to_group_index: 1, + to_row_index: 2, + }, + AssertRow { + group_index: 1, + row_index: 2, + row: group_1.rows.get(0).unwrap().clone(), + }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_test() { + let mut test = GridGroupTest::new().await; + let scripts = vec![MoveRow { + from_group_index: 0, + from_row_index: 0, + to_group_index: 1, + to_row_index: 1, + }]; + test.run_scripts(scripts).await; + + let group = test.group_at_index(1).await; + let scripts = vec![ + AssertGroupRowCount { + group_index: 1, + row_count: 3, + }, + MoveRow { + from_group_index: 1, + from_row_index: 2, + to_group_index: 1, + to_row_index: 0, + }, + AssertRow { + group_index: 1, + row_index: 0, + row: group.rows.get(2).unwrap().clone(), + }, + ]; + test.run_scripts(scripts).await; +} +#[tokio::test] +async fn group_create_row_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ CreateRow { group_index: 0 }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 3, }, CreateRow { group_index: 1 }, CreateRow { group_index: 1 }, - AssertGroup { + AssertGroupRowCount { group_index: 1, row_count: 4, }, @@ -121,14 +214,14 @@ async fn board_create_row_test() { } #[tokio::test] -async fn board_delete_row_test() { +async fn group_delete_row_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ DeleteRow { group_index: 0, row_index: 0, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 1, }, @@ -137,7 +230,7 @@ async fn board_delete_row_test() { } #[tokio::test] -async fn board_delete_all_row_test() { +async fn group_delete_all_row_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ DeleteRow { @@ -148,7 +241,7 @@ async fn board_delete_all_row_test() { group_index: 0, row_index: 0, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 0, }, @@ -157,7 +250,7 @@ async fn board_delete_all_row_test() { } #[tokio::test] -async fn board_update_row_test() { +async fn group_update_row_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ // Update the row at 0 in group0 by setting the row's group field data @@ -166,11 +259,11 @@ async fn board_update_row_test() { row_index: 0, to_group_index: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 1, row_count: 3, }, @@ -179,7 +272,7 @@ async fn board_update_row_test() { } #[tokio::test] -async fn board_reorder_group_test() { +async fn group_reorder_group_test() { let mut test = GridGroupTest::new().await; let scripts = vec![ // Update the row at 0 in group0 by setting the row's group field data @@ -188,14 +281,36 @@ async fn board_reorder_group_test() { row_index: 0, to_group_index: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 0, row_count: 1, }, - AssertGroup { + AssertGroupRowCount { group_index: 1, row_count: 3, }, ]; test.run_scripts(scripts).await; } + +#[tokio::test] +async fn group_move_group_test() { + let mut test = GridGroupTest::new().await; + let group_0 = test.group_at_index(0).await; + let group_1 = test.group_at_index(1).await; + let scripts = vec![ + MoveGroup { + from_group_index: 0, + to_group_index: 1, + }, + AssertGroup { + group_index: 0, + expected_group: group_1, + }, + AssertGroup { + group_index: 1, + expected_group: group_0, + }, + ]; + test.run_scripts(scripts).await; +} diff --git a/frontend/rust-lib/flowy-sdk/src/lib.rs b/frontend/rust-lib/flowy-sdk/src/lib.rs index ce5920e532..e0bd601987 100644 --- a/frontend/rust-lib/flowy-sdk/src/lib.rs +++ b/frontend/rust-lib/flowy-sdk/src/lib.rs @@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String { filters.push(format!("lib_ot={}", level)); filters.push(format!("lib_ws={}", level)); filters.push(format!("lib_infra={}", level)); - // filters.push(format!("flowy_sync={}", level)); + filters.push(format!("flowy_sync={}", level)); // filters.push(format!("flowy_revision={}", level)); // filters.push(format!("lib_dispatch={}", level)); diff --git a/shared-lib/flowy-error-code/src/code.rs b/shared-lib/flowy-error-code/src/code.rs index 63f7ac7749..c5e89a99f3 100644 --- a/shared-lib/flowy-error-code/src/code.rs +++ b/shared-lib/flowy-error-code/src/code.rs @@ -125,6 +125,9 @@ pub enum ErrorCode { #[display(fmt = "Invalid data")] InvalidData = 1000, + + #[display(fmt = "Out of bounds")] + OutOfBounds = 10001, } impl ErrorCode { diff --git a/shared-lib/flowy-grid-data-model/src/revision/filter_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/filter_rev.rs new file mode 100644 index 0000000000..7079b52229 --- /dev/null +++ b/shared-lib/flowy-grid-data-model/src/revision/filter_rev.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct FilterConfigurationRevision { + pub id: String, + pub field_id: String, + pub condition: u8, + pub content: Option, +} diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs index da366f39ab..1f52ae93e2 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs @@ -1,8 +1,7 @@ -use crate::revision::{FieldRevision, FieldTypeRevision}; +use crate::revision::{FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision}; use indexmap::IndexMap; use nanoid::nanoid; use serde::{Deserialize, Serialize}; -use serde_repr::*; use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; @@ -15,6 +14,7 @@ pub fn gen_grid_group_id() -> String { nanoid!(6) } +#[allow(dead_code)] pub fn gen_grid_sort_id() -> String { nanoid!(6) } @@ -24,120 +24,12 @@ pub type FilterConfigurationsByFieldId = HashMap; pub type GroupConfigurationsByFieldId = HashMap>>; -// -pub type SortConfiguration = Configuration; -pub type SortConfigurationsByFieldId = HashMap>>; -#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)] -pub struct SettingRevision { - pub layout: LayoutRevision, - - pub filters: FilterConfiguration, - - #[serde(default)] - pub groups: GroupConfiguration, - - #[serde(skip)] - pub sorts: SortConfiguration, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)] -#[repr(u8)] -pub enum LayoutRevision { - Table = 0, - Board = 1, -} - -impl ToString for LayoutRevision { - fn to_string(&self) -> String { - let layout_rev = self.clone() as u8; - layout_rev.to_string() - } -} - -impl std::default::Default for LayoutRevision { - fn default() -> Self { - LayoutRevision::Table - } -} - -impl SettingRevision { - pub fn get_all_groups(&self, field_revs: &[Arc]) -> Option { - self.groups.get_all_objects(field_revs) - } - - pub fn get_groups( - &self, - field_id: &str, - field_type_rev: &FieldTypeRevision, - ) -> Option>> { - self.groups.get_objects(field_id, field_type_rev) - } - - pub fn get_mut_groups( - &mut self, - field_id: &str, - field_type: &FieldTypeRevision, - ) -> Option<&mut Vec>> { - self.groups.get_mut_objects(field_id, field_type) - } - - pub fn insert_group( - &mut self, - field_id: &str, - field_type: &FieldTypeRevision, - group_rev: GroupConfigurationRevision, - ) { - // only one group can be set - self.groups.remove_all(); - self.groups.insert_object(field_id, field_type, group_rev); - } - - pub fn get_all_filters(&self, field_revs: &[Arc]) -> Option { - self.filters.get_all_objects(field_revs) - } - - pub fn get_filters( - &self, - field_id: &str, - field_type_rev: &FieldTypeRevision, - ) -> Option>> { - self.filters.get_objects(field_id, field_type_rev) - } - - pub fn get_mut_filters( - &mut self, - field_id: &str, - field_type: &FieldTypeRevision, - ) -> Option<&mut Vec>> { - self.filters.get_mut_objects(field_id, field_type) - } - - pub fn insert_filter( - &mut self, - field_id: &str, - field_type: &FieldTypeRevision, - filter_rev: FilterConfigurationRevision, - ) { - self.filters.insert_object(field_id, field_type, filter_rev); - } - - pub fn get_all_sort(&self) -> Option { - None - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] -pub struct SortConfigurationRevision { - pub id: String, - pub field_id: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(transparent)] pub struct Configuration where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { /// Key: field_id /// Value: this value contains key/value. @@ -149,7 +41,7 @@ where impl Configuration where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { pub fn get_mut_objects(&mut self, field_id: &str, field_type: &FieldTypeRevision) -> Option<&mut Vec>> { let value = self @@ -157,7 +49,7 @@ where .get_mut(field_id) .and_then(|object_rev_map| object_rev_map.get_mut(field_type)); if value.is_none() { - tracing::warn!("Can't find the {:?} with", std::any::type_name::()); + tracing::warn!("[Configuration] Can't find the {:?} with", std::any::type_name::()); } value } @@ -184,7 +76,8 @@ where Some(objects_by_field_id) } - pub fn insert_object(&mut self, field_id: &str, field_type: &FieldTypeRevision, object: T) { + /// add object to the end of the list + pub fn add_object(&mut self, field_id: &str, field_type: &FieldTypeRevision, object: T) { let object_rev_map = self .inner .entry(field_id.to_string()) @@ -196,16 +89,16 @@ where .push(Arc::new(object)) } - pub fn remove_all(&mut self) { + pub fn clear(&mut self) { self.inner.clear() } } -#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(transparent)] pub struct ObjectIndexMap where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { #[serde(with = "indexmap::serde_seq")] pub object_by_field_type: IndexMap>>, @@ -213,7 +106,7 @@ where impl ObjectIndexMap where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { pub fn new() -> Self { ObjectIndexMap::default() @@ -222,7 +115,7 @@ where impl std::ops::Deref for ObjectIndexMap where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { type Target = IndexMap>>; @@ -233,25 +126,9 @@ where impl std::ops::DerefMut for ObjectIndexMap where - T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static, + T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.object_by_field_type } } - -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] -pub struct GroupConfigurationRevision { - pub id: String, - pub field_id: String, - pub field_type_rev: FieldTypeRevision, - pub content: Option>, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] -pub struct FilterConfigurationRevision { - pub id: String, - pub field_id: String, - pub condition: u8, - pub content: Option, -} diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs index 2fcc2fc4eb..74c3c72511 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs @@ -1,24 +1,50 @@ -use crate::revision::SettingRevision; +use crate::revision::{FilterConfiguration, GroupConfiguration}; use nanoid::nanoid; use serde::{Deserialize, Serialize}; +use serde_repr::*; #[allow(dead_code)] pub fn gen_grid_view_id() -> String { nanoid!(6) } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum LayoutRevision { + Table = 0, + Board = 1, +} + +impl ToString for LayoutRevision { + fn to_string(&self) -> String { + let layout_rev = self.clone() as u8; + layout_rev.to_string() + } +} + +impl std::default::Default for LayoutRevision { + fn default() -> Self { + LayoutRevision::Table + } +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct GridViewRevision { pub view_id: String, pub grid_id: String, - pub setting: SettingRevision, + pub layout: LayoutRevision, - // For the moment, we just use the order returned from the GridRevision - #[allow(dead_code)] - #[serde(skip, rename = "row")] - pub row_orders: Vec, + #[serde(default)] + pub filters: FilterConfiguration, + + #[serde(default)] + pub groups: GroupConfiguration, + // // For the moment, we just use the order returned from the GridRevision + // #[allow(dead_code)] + // #[serde(skip, rename = "rows")] + // pub row_orders: Vec, } impl GridViewRevision { @@ -26,8 +52,10 @@ impl GridViewRevision { GridViewRevision { view_id, grid_id, - setting: Default::default(), - row_orders: vec![], + layout: Default::default(), + filters: Default::default(), + groups: Default::default(), + // row_orders: vec![], } } } @@ -36,3 +64,24 @@ impl GridViewRevision { pub struct RowOrderRevision { pub row_id: String, } + +#[cfg(test)] +mod tests { + use crate::revision::GridViewRevision; + + #[test] + fn grid_view_revision_serde_test() { + let grid_view_revision = GridViewRevision { + view_id: "1".to_string(), + grid_id: "1".to_string(), + layout: Default::default(), + filters: Default::default(), + groups: Default::default(), + }; + let s = serde_json::to_string(&grid_view_revision).unwrap(); + assert_eq!( + s, + r#"{"view_id":"1","grid_id":"1","layout":0,"filters":[],"groups":[]}"# + ); + } +} diff --git a/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs new file mode 100644 index 0000000000..97d45295fe --- /dev/null +++ b/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs @@ -0,0 +1,181 @@ +use crate::revision::{gen_grid_group_id, FieldTypeRevision}; +use serde::{Deserialize, Serialize}; +use serde_json::Error; +use serde_repr::*; + +pub trait GroupConfigurationContentSerde: Sized + Send + Sync { + fn from_configuration_content(s: &str) -> Result; + + fn to_configuration_content(&self) -> Result; +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct GroupConfigurationRevision { + pub id: String, + pub field_id: String, + pub field_type_rev: FieldTypeRevision, + pub groups: Vec, + pub content: String, +} + +impl GroupConfigurationRevision { + pub fn new(field_id: String, field_type: FieldTypeRevision, content: T) -> Result + where + T: GroupConfigurationContentSerde, + { + let content = content.to_configuration_content()?; + Ok(Self { + id: gen_grid_group_id(), + field_id, + field_type_rev: field_type, + groups: vec![], + content, + }) + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct TextGroupConfigurationRevision { + pub hide_empty: bool, +} + +impl GroupConfigurationContentSerde for TextGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct NumberGroupConfigurationRevision { + pub hide_empty: bool, +} + +impl GroupConfigurationContentSerde for NumberGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct UrlGroupConfigurationRevision { + pub hide_empty: bool, +} + +impl GroupConfigurationContentSerde for UrlGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct CheckboxGroupConfigurationRevision { + pub hide_empty: bool, +} + +impl GroupConfigurationContentSerde for CheckboxGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct SelectOptionGroupConfigurationRevision { + pub hide_empty: bool, +} + +impl GroupConfigurationContentSerde for SelectOptionGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct GroupRecordRevision { + pub group_id: String, + + #[serde(default = "DEFAULT_GROUP_RECORD_VISIBILITY")] + pub visible: bool, +} + +const DEFAULT_GROUP_RECORD_VISIBILITY: fn() -> bool = || true; + +impl GroupRecordRevision { + pub fn new(group_id: String) -> Self { + Self { + group_id, + visible: true, + } + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct DateGroupConfigurationRevision { + pub hide_empty: bool, + pub condition: DateCondition, +} + +impl GroupConfigurationContentSerde for DateGroupConfigurationRevision { + fn from_configuration_content(s: &str) -> Result { + serde_json::from_str(s) + } + fn to_configuration_content(&self) -> Result { + serde_json::to_string(self) + } +} + +#[derive(Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum DateCondition { + Relative = 0, + Day = 1, + Week = 2, + Month = 3, + Year = 4, +} + +impl std::default::Default for DateCondition { + fn default() -> Self { + DateCondition::Relative + } +} + +#[cfg(test)] +mod tests { + use crate::revision::{GroupConfigurationRevision, SelectOptionGroupConfigurationRevision}; + + #[test] + fn group_configuration_serde_test() { + let content = SelectOptionGroupConfigurationRevision { hide_empty: false }; + let rev = GroupConfigurationRevision::new("1".to_owned(), 2, content).unwrap(); + let json = serde_json::to_string(&rev).unwrap(); + + let rev: GroupConfigurationRevision = serde_json::from_str(&json).unwrap(); + let _content: SelectOptionGroupConfigurationRevision = serde_json::from_str(&rev.content).unwrap(); + } + + #[test] + fn group_configuration_serde_test2() { + let content = SelectOptionGroupConfigurationRevision { hide_empty: false }; + let content_json = serde_json::to_string(&content).unwrap(); + let rev = GroupConfigurationRevision::new("1".to_owned(), 2, content).unwrap(); + + assert_eq!(rev.content, content_json); + } +} diff --git a/shared-lib/flowy-grid-data-model/src/revision/mod.rs b/shared-lib/flowy-grid-data-model/src/revision/mod.rs index 7ea98d78e3..460370b86b 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/mod.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/mod.rs @@ -1,9 +1,13 @@ +mod filter_rev; mod grid_block; mod grid_rev; mod grid_setting_rev; mod grid_view; +mod group_rev; +pub use filter_rev::*; pub use grid_block::*; pub use grid_rev::*; pub use grid_setting_rev::*; pub use grid_view::*; +pub use group_rev::*; diff --git a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs index aad9c8b5f3..e079929aee 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs @@ -1,4 +1,3 @@ -use crate::entities::grid::FieldChangesetParams; use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_text_delta_from_revisions}; @@ -162,61 +161,6 @@ impl GridRevisionPad { }) } - pub fn update_field_rev( - &mut self, - changeset: FieldChangesetParams, - deserializer: T, - ) -> CollaborateResult> { - let field_id = changeset.field_id.clone(); - self.modify_field(&field_id, |field| { - let mut is_changed = None; - if let Some(name) = changeset.name { - field.name = name; - is_changed = Some(()) - } - - if let Some(desc) = changeset.desc { - field.desc = desc; - is_changed = Some(()) - } - - if let Some(field_type) = changeset.field_type { - field.ty = field_type; - is_changed = Some(()) - } - - if let Some(frozen) = changeset.frozen { - field.frozen = frozen; - is_changed = Some(()) - } - - if let Some(visibility) = changeset.visibility { - field.visibility = visibility; - is_changed = Some(()) - } - - if let Some(width) = changeset.width { - field.width = width; - is_changed = Some(()) - } - - if let Some(type_option_data) = changeset.type_option_data { - match deserializer.deserialize(type_option_data) { - Ok(json_str) => { - let field_type = field.ty; - field.insert_type_option_str(&field_type, json_str); - is_changed = Some(()) - } - Err(err) => { - tracing::error!("Deserialize data to type option json failed: {}", err); - } - } - } - - Ok(is_changed) - }) - } - pub fn get_field_rev(&self, field_id: &str) -> Option<(usize, &Arc)> { self.grid_rev .fields @@ -399,7 +343,7 @@ impl GridRevisionPad { ) } - fn modify_field(&mut self, field_id: &str, f: F) -> CollaborateResult> + pub fn modify_field(&mut self, field_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut FieldRevision) -> CollaborateResult>, { diff --git a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs index dd6cc6f977..ddc9c6e05a 100644 --- a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs @@ -1,11 +1,9 @@ -use crate::entities::grid::{CreateGridFilterParams, CreateGridGroupParams, GridSettingChangesetParams}; use crate::entities::revision::{md5, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_text_delta_from_revisions}; use flowy_grid_data_model::revision::{ - gen_grid_filter_id, gen_grid_group_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, - FilterConfigurationsByFieldId, GridViewRevision, GroupConfigurationRevision, GroupConfigurationsByFieldId, - SettingRevision, SortConfigurationsByFieldId, + FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision, + GroupConfigurationRevision, GroupConfigurationsByFieldId, }; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder}; use std::sync::Arc; @@ -50,71 +48,11 @@ impl GridViewRevisionPad { Self::from_delta(delta) } - pub fn get_setting_rev(&self) -> &SettingRevision { - &self.view.setting - } - - pub fn update_setting( - &mut self, - changeset: GridSettingChangesetParams, - ) -> CollaborateResult> { - self.modify(|view| { - let mut is_changed = None; - if let Some(params) = changeset.insert_filter { - view.setting.filters.insert_object( - ¶ms.field_id, - ¶ms.field_type_rev, - make_filter_revision(¶ms), - ); - is_changed = Some(()) - } - if let Some(params) = changeset.delete_filter { - if let Some(filters) = view - .setting - .filters - .get_mut_objects(¶ms.field_id, ¶ms.field_type_rev) - { - filters.retain(|filter| filter.id != params.filter_id); - is_changed = Some(()) - } - } - if let Some(params) = changeset.insert_group { - view.setting.groups.remove_all(); - view.setting.groups.insert_object( - ¶ms.field_id, - ¶ms.field_type_rev, - make_group_revision(¶ms), - ); - - is_changed = Some(()); - } - if let Some(params) = changeset.delete_group { - if let Some(groups) = view - .setting - .groups - .get_mut_objects(¶ms.field_id, ¶ms.field_type_rev) - { - groups.retain(|group| group.id != params.group_id); - is_changed = Some(()); - } - } - - Ok(is_changed) - }) - } - pub fn get_all_groups(&self, field_revs: &[Arc]) -> Option { - self.setting.groups.get_all_objects(field_revs) - } - - pub fn get_groups( - &self, - field_id: &str, - field_type_rev: &FieldTypeRevision, - ) -> Option>> { - self.setting.groups.get_objects(field_id, field_type_rev) + self.groups.get_all_objects(field_revs) } + #[tracing::instrument(level = "trace", skip_all, err)] pub fn insert_group( &mut self, field_id: &str, @@ -122,13 +60,40 @@ impl GridViewRevisionPad { group_rev: GroupConfigurationRevision, ) -> CollaborateResult> { self.modify(|view| { - // only one group can be set - view.setting.groups.remove_all(); - view.setting.groups.insert_object(field_id, field_type, group_rev); + // Only save one group + view.groups.clear(); + view.groups.add_object(field_id, field_type, group_rev); Ok(Some(())) }) } + #[tracing::instrument(level = "trace", skip_all)] + pub fn contains_group(&self, field_id: &str, field_type: &FieldTypeRevision) -> bool { + self.view.groups.get_objects(field_id, field_type).is_some() + } + + #[tracing::instrument(level = "trace", skip_all, err)] + pub fn with_mut_group( + &mut self, + field_id: &str, + field_type: &FieldTypeRevision, + configuration_id: &str, + mut_configuration_fn: F, + ) -> CollaborateResult> { + self.modify(|view| match view.groups.get_mut_objects(field_id, field_type) { + None => Ok(None), + Some(configurations_revs) => { + for configuration_rev in configurations_revs { + if configuration_rev.id == configuration_id { + mut_configuration_fn(Arc::make_mut(configuration_rev)); + return Ok(Some(())); + } + } + Ok(None) + } + }) + } + pub fn delete_group( &mut self, field_id: &str, @@ -136,7 +101,7 @@ impl GridViewRevisionPad { group_id: &str, ) -> CollaborateResult> { self.modify(|view| { - if let Some(groups) = view.setting.groups.get_mut_objects(field_id, field_type) { + if let Some(groups) = view.groups.get_mut_objects(field_id, field_type) { groups.retain(|group| group.id != group_id); Ok(Some(())) } else { @@ -146,7 +111,7 @@ impl GridViewRevisionPad { } pub fn get_all_filters(&self, field_revs: &[Arc]) -> Option { - self.setting.filters.get_all_objects(field_revs) + self.filters.get_all_objects(field_revs) } pub fn get_filters( @@ -154,7 +119,7 @@ impl GridViewRevisionPad { field_id: &str, field_type_rev: &FieldTypeRevision, ) -> Option>> { - self.setting.filters.get_objects(field_id, field_type_rev) + self.filters.get_objects(field_id, field_type_rev) } pub fn insert_filter( @@ -164,7 +129,7 @@ impl GridViewRevisionPad { filter_rev: FilterConfigurationRevision, ) -> CollaborateResult> { self.modify(|view| { - view.setting.filters.insert_object(field_id, field_type, filter_rev); + view.filters.add_object(field_id, field_type, filter_rev); Ok(Some(())) }) } @@ -176,7 +141,7 @@ impl GridViewRevisionPad { filter_id: &str, ) -> CollaborateResult> { self.modify(|view| { - if let Some(filters) = view.setting.filters.get_mut_objects(field_id, field_type) { + if let Some(filters) = view.filters.get_mut_objects(field_id, field_type) { filters.retain(|filter| filter.id != filter_id); Ok(Some(())) } else { @@ -185,10 +150,6 @@ impl GridViewRevisionPad { }) } - pub fn get_all_sort(&self) -> Option { - None - } - pub fn json_str(&self) -> CollaborateResult { make_grid_view_rev_json_str(&self.view) } @@ -216,24 +177,7 @@ impl GridViewRevisionPad { } } -fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision { - FilterConfigurationRevision { - id: gen_grid_filter_id(), - field_id: params.field_id.clone(), - condition: params.condition, - content: params.content.clone(), - } -} - -fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision { - GroupConfigurationRevision { - id: gen_grid_group_id(), - field_id: params.field_id.clone(), - field_type_rev: params.field_type_rev, - content: params.content.clone(), - } -} - +#[derive(Debug)] pub struct GridViewRevisionChangeset { pub delta: TextDelta, pub md5: String, diff --git a/shared-lib/flowy-sync/src/entities/grid.rs b/shared-lib/flowy-sync/src/entities/grid.rs deleted file mode 100644 index 3be3d98267..0000000000 --- a/shared-lib/flowy-sync/src/entities/grid.rs +++ /dev/null @@ -1,67 +0,0 @@ -use flowy_grid_data_model::revision::{FieldTypeRevision, LayoutRevision}; - -pub struct GridSettingChangesetParams { - pub grid_id: String, - pub layout_type: LayoutRevision, - pub insert_filter: Option, - pub delete_filter: Option, - pub insert_group: Option, - pub delete_group: Option, - pub insert_sort: Option, - pub delete_sort: Option, -} - -impl GridSettingChangesetParams { - pub fn is_filter_changed(&self) -> bool { - self.insert_filter.is_some() || self.delete_filter.is_some() - } -} -pub struct CreateGridFilterParams { - pub field_id: String, - pub field_type_rev: FieldTypeRevision, - pub condition: u8, - pub content: Option, -} - -pub struct DeleteFilterParams { - pub field_id: String, - pub filter_id: String, - pub field_type_rev: FieldTypeRevision, -} - -pub struct CreateGridGroupParams { - pub field_id: String, - pub field_type_rev: FieldTypeRevision, - pub content: Option>, -} - -pub struct DeleteGroupParams { - pub field_id: String, - pub group_id: String, - pub field_type_rev: FieldTypeRevision, -} - -pub struct CreateGridSortParams { - pub field_id: Option, -} - -#[derive(Debug, Clone, Default)] -pub struct FieldChangesetParams { - pub field_id: String, - - pub grid_id: String, - - pub name: Option, - - pub desc: Option, - - pub field_type: Option, - - pub frozen: Option, - - pub visibility: Option, - - pub width: Option, - - pub type_option_data: Option>, -} diff --git a/shared-lib/flowy-sync/src/entities/mod.rs b/shared-lib/flowy-sync/src/entities/mod.rs index 768640454b..1c357df94c 100644 --- a/shared-lib/flowy-sync/src/entities/mod.rs +++ b/shared-lib/flowy-sync/src/entities/mod.rs @@ -1,5 +1,4 @@ pub mod folder; -pub mod grid; pub mod parser; pub mod revision; pub mod text_block;