瀏覽代碼

Merge pull request #579 from AppFlowy-IO/feat/grid_task_runner

Nathan.fooo 2 年之前
父節點
當前提交
6554b8354b
共有 69 個文件被更改,包括 2051 次插入726 次删除
  1. 90 0
      frontend/app_flowy/lib/workspace/application/grid/block/block_listener.dart
  2. 0 0
      frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart
  3. 17 17
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_cache.dart
  4. 0 0
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart
  5. 0 0
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart
  6. 3 4
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  7. 3 3
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart
  8. 22 3
      frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart
  9. 0 42
      frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart
  10. 1 1
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  11. 25 34
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  12. 7 0
      frontend/rust-lib/Cargo.lock
  13. 1 0
      frontend/rust-lib/flowy-grid/Cargo.toml
  14. 1 1
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  15. 8 13
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  16. 4 4
      frontend/rust-lib/flowy-grid/src/event_map.rs
  17. 44 33
      frontend/rust-lib/flowy-grid/src/manager.rs
  18. 29 21
      frontend/rust-lib/flowy-grid/src/services/block_manager.rs
  19. 6 6
      frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs
  20. 7 25
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  21. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  22. 58 0
      frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs
  23. 3 0
      frontend/rust-lib/flowy-grid/src/services/filter/mod.rs
  24. 62 35
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  25. 21 0
      frontend/rust-lib/flowy-grid/src/services/grid_editor_task.rs
  26. 5 0
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  27. 14 15
      frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs
  28. 28 36
      frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs
  29. 3 0
      frontend/rust-lib/flowy-grid/src/services/setting/mod.rs
  30. 35 0
      frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs
  31. 3 0
      frontend/rust-lib/flowy-grid/src/services/snapshot/mod.rs
  32. 7 0
      frontend/rust-lib/flowy-grid/src/services/snapshot/snapshot_service.rs
  33. 9 0
      frontend/rust-lib/flowy-grid/src/services/tasks/mod.rs
  34. 119 0
      frontend/rust-lib/flowy-grid/src/services/tasks/queue.rs
  35. 45 0
      frontend/rust-lib/flowy-grid/src/services/tasks/runner.rs
  36. 87 0
      frontend/rust-lib/flowy-grid/src/services/tasks/scheduler.rs
  37. 32 0
      frontend/rust-lib/flowy-grid/src/services/tasks/store.rs
  38. 69 0
      frontend/rust-lib/flowy-grid/src/services/tasks/task.rs
  39. 8 8
      frontend/rust-lib/flowy-grid/tests/grid/block_test.rs
  40. 2 1
      frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs
  41. 1 0
      frontend/rust-lib/flowy-grid/tests/grid/field_test.rs
  42. 81 0
      frontend/rust-lib/flowy-grid/tests/grid/field_util.rs
  43. 43 0
      frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs
  44. 3 1
      frontend/rust-lib/flowy-grid/tests/grid/mod.rs
  45. 15 14
      frontend/rust-lib/flowy-grid/tests/grid/row_test.rs
  46. 72 0
      frontend/rust-lib/flowy-grid/tests/grid/row_util.rs
  47. 66 94
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  48. 0 1
      frontend/rust-lib/flowy-grid/tests/grid/setting_test.rs
  49. 2 2
      frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
  50. 9 0
      frontend/rust-lib/flowy-text-block/src/editor.rs
  51. 1 0
      frontend/scripts/flowy-tool/src/proto/template/proto_file/struct_template.rs
  52. 1 1
      frontend/scripts/makefile/tool.toml
  53. 2 5
      shared-lib/flowy-folder-data-model/src/revision/view_rev.rs
  54. 1 1
      shared-lib/flowy-grid-data-model/Cargo.toml
  55. 11 28
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  56. 328 0
      shared-lib/flowy-grid-data-model/src/entities/grid_filter.rs
  57. 80 0
      shared-lib/flowy-grid-data-model/src/entities/grid_group.rs
  58. 102 36
      shared-lib/flowy-grid-data-model/src/entities/grid_setting.rs
  59. 68 0
      shared-lib/flowy-grid-data-model/src/entities/grid_sort.rs
  60. 6 0
      shared-lib/flowy-grid-data-model/src/entities/mod.rs
  61. 0 58
      shared-lib/flowy-grid-data-model/src/parser/grid_info_parser.rs
  62. 0 3
      shared-lib/flowy-grid-data-model/src/parser/mod.rs
  63. 94 0
      shared-lib/flowy-grid-data-model/src/revision/grid_filter_rev.rs
  64. 23 16
      shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs
  65. 28 73
      shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs
  66. 31 35
      shared-lib/flowy-sync/src/client_grid/grid_block_revsion_pad.rs
  67. 12 11
      shared-lib/flowy-sync/src/client_grid/grid_builder.rs
  68. 87 37
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  69. 4 6
      shared-lib/flowy-user-data-model/src/entities/user_setting.rs

+ 90 - 0
frontend/app_flowy/lib/workspace/application/grid/block/block_listener.dart

@@ -0,0 +1,90 @@
+import 'dart:async';
+import 'dart:collection';
+import 'dart:typed_data';
+
+import 'package:app_flowy/core/notification_helper.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_infra/notifier.dart';
+import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
+
+class GridBlockCache {
+  final String gridId;
+  void Function(GridBlockUpdateNotifierValue)? _onBlockChanged;
+
+  final LinkedHashMap<String, _GridBlockListener> _listeners = LinkedHashMap();
+  GridBlockCache({required this.gridId});
+
+  void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
+    _onBlockChanged = onBlockChanged;
+    for (final listener in _listeners.values) {
+      listener.start(onBlockChanged);
+    }
+  }
+
+  Future<void> dispose() async {
+    for (final listener in _listeners.values) {
+      await listener.stop();
+    }
+  }
+
+  void addBlockListener(String blockId) {
+    if (_onBlockChanged == null) {
+      Log.error("Should call start() first");
+      return;
+    }
+    if (_listeners.containsKey(blockId)) {
+      Log.error("Duplicate block listener");
+      return;
+    }
+
+    final listener = _GridBlockListener(blockId: blockId);
+    listener.start(_onBlockChanged!);
+    _listeners[blockId] = listener;
+  }
+}
+
+typedef GridBlockUpdateNotifierValue = Either<List<GridRowsChangeset>, FlowyError>;
+
+class _GridBlockListener {
+  final String blockId;
+  PublishNotifier<GridBlockUpdateNotifierValue>? _rowsUpdateNotifier = PublishNotifier();
+  GridNotificationListener? _listener;
+
+  _GridBlockListener({required this.blockId});
+
+  void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
+    if (_listener != null) {
+      _listener?.stop();
+    }
+
+    _listener = GridNotificationListener(
+      objectId: blockId,
+      handler: _handler,
+    );
+
+    _rowsUpdateNotifier?.addPublishListener(onBlockChanged);
+  }
+
+  void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
+    switch (ty) {
+      case GridNotification.DidUpdateGridBlock:
+        result.fold(
+          (payload) => _rowsUpdateNotifier?.value = left([GridRowsChangeset.fromBuffer(payload)]),
+          (error) => _rowsUpdateNotifier?.value = right(error),
+        );
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  Future<void> stop() async {
+    await _listener?.stop();
+    _rowsUpdateNotifier?.dispose();
+    _rowsUpdateNotifier = null;
+  }
+}

+ 0 - 0
frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart


+ 17 - 17
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_cache.dart → frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_cache.dart

@@ -2,21 +2,21 @@ part of 'cell_service.dart';
 
 typedef GridCellMap = LinkedHashMap<String, GridCell>;
 
-class GridCellCacheData {
-  GridCellCacheKey key;
+class _GridCellCacheObject {
+  _GridCellCacheKey key;
   dynamic object;
-  GridCellCacheData({
+  _GridCellCacheObject({
     required this.key,
     required this.object,
   });
 }
 
-class GridCellCacheKey {
+class _GridCellCacheKey {
   final String fieldId;
-  final String objectId;
-  GridCellCacheKey({
+  final String rowId;
+  _GridCellCacheKey({
     required this.fieldId,
-    required this.objectId,
+    required this.rowId,
   });
 }
 
@@ -51,46 +51,46 @@ class GridCellCache {
     });
   }
 
-  void addFieldListener(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
+  void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
     var map = _fieldListenerByFieldId[cacheKey.fieldId];
     if (map == null) {
       _fieldListenerByFieldId[cacheKey.fieldId] = {};
       map = _fieldListenerByFieldId[cacheKey.fieldId];
-      map![cacheKey.objectId] = [onFieldChanged];
+      map![cacheKey.rowId] = [onFieldChanged];
     } else {
-      var objects = map[cacheKey.objectId];
+      var objects = map[cacheKey.rowId];
       if (objects == null) {
-        map[cacheKey.objectId] = [onFieldChanged];
+        map[cacheKey.rowId] = [onFieldChanged];
       } else {
         objects.add(onFieldChanged);
       }
     }
   }
 
-  void removeFieldListener(GridCellCacheKey cacheKey, VoidCallback fn) {
-    var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId];
+  void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) {
+    var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
     final index = callbacks?.indexWhere((callback) => callback == fn);
     if (index != null && index != -1) {
       callbacks?.removeAt(index);
     }
   }
 
-  void insert<T extends GridCellCacheData>(T item) {
+  void insert<T extends _GridCellCacheObject>(T item) {
     var map = _cellDataByFieldId[item.key.fieldId];
     if (map == null) {
       _cellDataByFieldId[item.key.fieldId] = {};
       map = _cellDataByFieldId[item.key.fieldId];
     }
 
-    map![item.key.objectId] = item.object;
+    map![item.key.rowId] = item.object;
   }
 
-  T? get<T>(GridCellCacheKey key) {
+  T? get<T>(_GridCellCacheKey key) {
     final map = _cellDataByFieldId[key.fieldId];
     if (map == null) {
       return null;
     } else {
-      final object = map[key.objectId];
+      final object = map[key.rowId];
       if (object is T) {
         return object;
       } else {

+ 0 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart → frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart


+ 0 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart → frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart


+ 3 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

@@ -19,10 +19,10 @@ import 'package:app_flowy/workspace/application/grid/cell/select_option_service.
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'dart:convert' show utf8;
 part 'cell_service.freezed.dart';
-part 'data_loader.dart';
+part 'cell_data_loader.dart';
 part 'context_builder.dart';
-part 'data_cache.dart';
-part 'data_persistence.dart';
+part 'cell_data_cache.dart';
+part 'cell_data_persistence.dart';
 
 // key: rowId
 
@@ -62,7 +62,6 @@ class GridCell with _$GridCell {
     required String gridId,
     required String rowId,
     required Field field,
-    Cell? cell,
   }) = _GridCell;
 
   // ignore: unused_element

+ 3 - 3
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart

@@ -100,7 +100,7 @@ class GridCellContextBuilder {
 class _GridCellContext<T, D> extends Equatable {
   final GridCell gridCell;
   final GridCellCache cellCache;
-  final GridCellCacheKey _cacheKey;
+  final _GridCellCacheKey _cacheKey;
   final IGridCellDataLoader<T> cellDataLoader;
   final _GridCellDataPersistence<D> cellDataPersistence;
   final FieldService _fieldService;
@@ -118,7 +118,7 @@ class _GridCellContext<T, D> extends Equatable {
     required this.cellDataLoader,
     required this.cellDataPersistence,
   })  : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
-        _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id);
+        _cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
 
   _GridCellContext<T, D> clone() {
     return _GridCellContext(
@@ -213,7 +213,7 @@ class _GridCellContext<T, D> extends Equatable {
     _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
       cellDataLoader.loadData().then((data) {
         _cellDataNotifier?.value = data;
-        cellCache.insert(GridCellCacheData(key: _cacheKey, object: data));
+        cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
       });
     });
   }

+ 22 - 3
frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart

@@ -1,11 +1,13 @@
 import 'dart:async';
 import 'package:dartz/dartz.dart';
 import 'package:equatable/equatable.dart';
+import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
+import 'block/block_listener.dart';
 import 'cell/cell_service/cell_service.dart';
 import 'grid_service.dart';
 import 'row/row_service.dart';
@@ -19,9 +21,12 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   late final GridRowCache rowCache;
   late final GridCellCache cellCache;
 
+  final GridBlockCache blockCache;
+
   GridBloc({required View view})
       : _gridService = GridService(gridId: view.id),
         fieldCache = GridFieldCache(gridId: view.id),
+        blockCache = GridBlockCache(gridId: view.id),
         super(GridState.initial(view.id)) {
     rowCache = GridRowCache(
       gridId: view.id,
@@ -33,6 +38,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
       fieldDelegate: GridCellCacheDelegateImpl(fieldCache),
     );
 
+    blockCache.start((result) {
+      result.fold(
+        (changesets) => rowCache.applyChangesets(changesets),
+        (err) => Log.error(err),
+      );
+    });
+
     on<GridEvent>(
       (event, emit) async {
         await event.when(
@@ -60,6 +72,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
     await cellCache.dispose();
     await rowCache.dispose();
     await fieldCache.dispose();
+    await blockCache.dispose();
     return super.close();
   }
 
@@ -79,7 +92,15 @@ class GridBloc extends Bloc<GridEvent, GridState> {
     final result = await _gridService.loadGrid();
     return Future(
       () => result.fold(
-        (grid) async => await _loadFields(grid, emit),
+        (grid) async {
+          for (final block in grid.blocks) {
+            blockCache.addBlockListener(block.id);
+          }
+          final rowOrders = grid.blocks.expand((block) => block.rowOrders).toList();
+          rowCache.initialRows(rowOrders);
+
+          await _loadFields(grid, emit);
+        },
         (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
       ),
     );
@@ -91,7 +112,6 @@ class GridBloc extends Bloc<GridEvent, GridState> {
       () => result.fold(
         (fields) {
           fieldCache.fields = fields.items;
-          rowCache.resetRows(grid.blockOrders);
 
           emit(state.copyWith(
             grid: Some(grid),
@@ -143,7 +163,6 @@ class GridLoadingState with _$GridLoadingState {
 
 class GridFieldEquatable extends Equatable {
   final List<Field> _fields;
-
   const GridFieldEquatable(List<Field> fields) : _fields = fields;
 
   @override

+ 0 - 42
frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart

@@ -1,42 +0,0 @@
-import 'package:dartz/dartz.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
-import 'package:flowy_infra/notifier.dart';
-import 'dart:async';
-import 'dart:typed_data';
-import 'package:app_flowy/core/notification_helper.dart';
-
-class GridRowListener {
-  final String gridId;
-  PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
-  GridNotificationListener? _listener;
-
-  GridRowListener({required this.gridId});
-
-  void start() {
-    _listener = GridNotificationListener(
-      objectId: gridId,
-      handler: _handler,
-    );
-  }
-
-  void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
-    switch (ty) {
-      case GridNotification.DidUpdateGridRow:
-        result.fold(
-          (payload) => rowsUpdateNotifier.value = left([GridRowsChangeset.fromBuffer(payload)]),
-          (error) => rowsUpdateNotifier.value = right(error),
-        );
-        break;
-
-      default:
-        break;
-    }
-  }
-
-  Future<void> stop() async {
-    await _listener?.stop();
-    rowsUpdateNotifier.dispose();
-  }
-}

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/grid_service.dart

@@ -22,7 +22,7 @@ class GridService {
     await FolderEventSetLatestView(ViewId(value: gridId)).send();
 
     final payload = GridId(value: gridId);
-    return GridEventGetGridData(payload).send();
+    return GridEventGetGrid(payload).send();
   }
 
   Future<Either<Row, FlowyError>> createRow({Option<String>? startRowId}) {

+ 25 - 34
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -10,7 +10,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:app_flowy/workspace/application/grid/grid_listener.dart';
 part 'row_service.freezed.dart';
 
 typedef RowUpdateCallback = void Function();
@@ -24,7 +23,6 @@ abstract class GridRowFieldDelegate {
 class GridRowCache {
   final String gridId;
   final RowsNotifier _rowsNotifier;
-  final GridRowListener _rowsListener;
   final GridRowFieldDelegate _fieldDelegate;
   List<GridRow> get clonedRows => _rowsNotifier.clonedRows;
 
@@ -39,32 +37,23 @@ class GridRowCache {
             );
           },
         ),
-        _rowsListener = GridRowListener(gridId: gridId),
         _fieldDelegate = fieldDelegate {
     //
     fieldDelegate.onFieldChanged(() => _rowsNotifier.fieldDidChange());
-
-    // listen on the row update
-    _rowsListener.rowsUpdateNotifier.addPublishListener((result) {
-      result.fold(
-        (changesets) {
-          for (final changeset in changesets) {
-            _rowsNotifier.deleteRows(changeset.deletedRows);
-            _rowsNotifier.insertRows(changeset.insertedRows);
-            _rowsNotifier.updateRows(changeset.updatedRows);
-          }
-        },
-        (err) => Log.error(err),
-      );
-    });
-    _rowsListener.start();
   }
 
   Future<void> dispose() async {
-    await _rowsListener.stop();
     _rowsNotifier.dispose();
   }
 
+  void applyChangesets(List<GridRowsChangeset> changesets) {
+    for (final changeset in changesets) {
+      _rowsNotifier.deleteRows(changeset.deletedRows);
+      _rowsNotifier.insertRows(changeset.insertedRows);
+      _rowsNotifier.updateRows(changeset.updatedRows);
+    }
+  }
+
   void addListener({
     void Function(List<GridRow>, GridRowChangeReason)? onChanged,
     bool Function()? listenWhen,
@@ -130,9 +119,8 @@ class GridRowCache {
     return _makeGridCells(rowId, data);
   }
 
-  void resetRows(List<GridBlockOrder> blocks) {
-    final rowOrders = blocks.expand((block) => block.rowOrders).toList();
-    _rowsNotifier.reset(rowOrders);
+  void initialRows(List<RowOrder> rowOrders) {
+    _rowsNotifier.initialRows(rowOrders);
   }
 
   Future<void> _loadRow(String rowId) async {
@@ -142,7 +130,11 @@ class GridRowCache {
 
     final result = await GridEventGetRow(payload).send();
     result.fold(
-      (rowData) => _rowsNotifier.rowData = rowData,
+      (rowData) {
+        if (rowData.hasRow()) {
+          _rowsNotifier.rowData = rowData.row;
+        }
+      },
       (err) => Log.error(err),
     );
   }
@@ -154,7 +146,6 @@ class GridRowCache {
         cellDataMap[field.id] = GridCell(
           rowId: rowId,
           gridId: gridId,
-          cell: row?.cellByFieldId[field.id],
           field: field,
         );
       }
@@ -173,7 +164,9 @@ class RowsNotifier extends ChangeNotifier {
     required this.rowBuilder,
   });
 
-  void reset(List<RowOrder> rowOrders) {
+  List<GridRow> get clonedRows => [..._rows];
+
+  void initialRows(List<RowOrder> rowOrders) {
     _rowDataMap = HashMap();
     final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList();
     _update(rows, const GridRowChangeReason.initial());
@@ -199,20 +192,20 @@ class RowsNotifier extends ChangeNotifier {
     _update(newRows, GridRowChangeReason.delete(deletedIndex));
   }
 
-  void insertRows(List<IndexRowOrder> createdRows) {
-    if (createdRows.isEmpty) {
+  void insertRows(List<IndexRowOrder> insertRows) {
+    if (insertRows.isEmpty) {
       return;
     }
 
     InsertedIndexs insertIndexs = [];
     final List<GridRow> newRows = clonedRows;
-    for (final createdRow in createdRows) {
+    for (final insertRow in insertRows) {
       final insertIndex = InsertedIndex(
-        index: createdRow.index,
-        rowId: createdRow.rowOrder.rowId,
+        index: insertRow.index,
+        rowId: insertRow.rowOrder.rowId,
       );
       insertIndexs.add(insertIndex);
-      newRows.insert(createdRow.index, (rowBuilder(createdRow.rowOrder)));
+      newRows.insert(insertRow.index, (rowBuilder(insertRow.rowOrder)));
     }
     _update(newRows, GridRowChangeReason.insert(insertIndexs));
   }
@@ -281,8 +274,6 @@ class RowsNotifier extends ChangeNotifier {
   Row? rowDataWithId(String rowId) {
     return _rowDataMap[rowId];
   }
-
-  List<GridRow> get clonedRows => [..._rows];
 }
 
 class RowService {
@@ -310,7 +301,7 @@ class RowService {
     return GridEventMoveItem(payload).send();
   }
 
-  Future<Either<Row, FlowyError>> getRow() {
+  Future<Either<OptionalRow, FlowyError>> getRow() {
     final payload = RowIdentifierPayload.create()
       ..gridId = gridId
       ..rowId = rowId;

+ 7 - 0
frontend/rust-lib/Cargo.lock

@@ -93,6 +93,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "atomic_refcell"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -925,6 +931,7 @@ dependencies = [
 name = "flowy-grid"
 version = "0.1.0"
 dependencies = [
+ "atomic_refcell",
  "bytes",
  "chrono",
  "dart-notify",

+ 1 - 0
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -39,6 +39,7 @@ fancy-regex = "0.10.0"
 regex = "1.5.6"
 url = { version = "2"}
 futures = "0.3.15"
+atomic_refcell = "0.1.8"
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }

+ 1 - 1
frontend/rust-lib/flowy-grid/src/dart_notification.rs

@@ -6,7 +6,7 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
 pub enum GridNotification {
     Unknown = 0,
     DidCreateBlock = 11,
-    DidUpdateGridRow = 20,
+    DidUpdateGridBlock = 20,
     DidUpdateGridField = 21,
     DidUpdateRow = 30,
     DidUpdateCell = 40,

+ 8 - 13
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -9,7 +9,7 @@ use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
-pub(crate) async fn get_grid_data_handler(
+pub(crate) async fn get_grid_handler(
     data: Data<GridId>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<Grid, FlowyError> {
@@ -27,7 +27,7 @@ pub(crate) async fn get_grid_setting_handler(
     let grid_id: GridId = data.into_inner();
     let editor = manager.open_grid(grid_id).await?;
     let grid_setting = editor.get_grid_setting().await?;
-    data_result(grid_setting.into())
+    data_result(grid_setting)
 }
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
@@ -48,12 +48,7 @@ pub(crate) async fn get_grid_blocks_handler(
 ) -> DataResult<RepeatedGridBlock, FlowyError> {
     let params: QueryGridBlocksParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    let block_ids = params
-        .block_orders
-        .into_iter()
-        .map(|block| block.block_id)
-        .collect::<Vec<String>>();
-    let repeated_grid_block = editor.get_blocks(Some(block_ids)).await?;
+    let repeated_grid_block = editor.get_blocks(Some(params.block_ids)).await?;
     data_result(repeated_grid_block)
 }
 
@@ -220,13 +215,13 @@ async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType)
 pub(crate) async fn get_row_handler(
     data: Data<RowIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
-) -> DataResult<Row, FlowyError> {
+) -> DataResult<OptionalRow, FlowyError> {
     let params: RowIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor.get_row(&params.row_id).await? {
-        None => Err(FlowyError::record_not_found().context("Can not find the row")),
-        Some(row) => data_result(row),
-    }
+    let row = OptionalRow {
+        row: editor.get_row(&params.row_id).await?,
+    };
+    data_result(row)
 }
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]

+ 4 - 4
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -8,10 +8,10 @@ use strum_macros::Display;
 pub fn create(grid_manager: Arc<GridManager>) -> Module {
     let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(grid_manager);
     module = module
-        .event(GridEvent::GetGridData, get_grid_data_handler)
+        .event(GridEvent::GetGrid, get_grid_handler)
         .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
         .event(GridEvent::GetGridSetting, get_grid_setting_handler)
-        .event(GridEvent::UpdateGridSetting, get_grid_setting_handler)
+        .event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
         // Field
         .event(GridEvent::GetFields, get_fields_handler)
         .event(GridEvent::UpdateField, update_field_handler)
@@ -46,7 +46,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
 #[event_err = "FlowyError"]
 pub enum GridEvent {
     #[event(input = "GridId", output = "Grid")]
-    GetGridData = 0,
+    GetGrid = 0,
 
     #[event(input = "QueryGridBlocksPayload", output = "RepeatedGridBlock")]
     GetGridBlocks = 1,
@@ -99,7 +99,7 @@ pub enum GridEvent {
     #[event(input = "CreateRowPayload", output = "Row")]
     CreateRow = 50,
 
-    #[event(input = "RowIdentifierPayload", output = "Row")]
+    #[event(input = "RowIdentifierPayload", output = "OptionalRow")]
     GetRow = 51,
 
     #[event(input = "RowIdentifierPayload")]

+ 44 - 33
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -2,16 +2,18 @@ use crate::services::grid_editor::GridRevisionEditor;
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::persistence::kv::GridKVPersistence;
 use crate::services::persistence::GridDatabase;
+use crate::services::tasks::GridTaskScheduler;
 use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_database::ConnectionPool;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridSettingRevision};
+use flowy_grid_data_model::revision::{BuildGridContext, GridRevision};
 use flowy_revision::disk::{SQLiteGridBlockMetaRevisionPersistence, SQLiteGridRevisionPersistence};
 use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket};
 use flowy_sync::client_grid::{make_block_meta_delta, make_grid_delta};
 use flowy_sync::entities::revision::{RepeatedRevision, Revision};
 use std::sync::Arc;
+use tokio::sync::RwLock;
 
 pub trait GridUser: Send + Sync {
     fn user_id(&self) -> Result<String, FlowyError>;
@@ -19,12 +21,15 @@ pub trait GridUser: Send + Sync {
     fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError>;
 }
 
+pub type GridTaskSchedulerRwLock = Arc<RwLock<GridTaskScheduler>>;
+
 pub struct GridManager {
-    editor_map: Arc<DashMap<String, Arc<GridRevisionEditor>>>,
+    grid_editors: Arc<DashMap<String, Arc<GridRevisionEditor>>>,
     grid_user: Arc<dyn GridUser>,
     block_index_cache: Arc<BlockIndexCache>,
     #[allow(dead_code)]
     kv_persistence: Arc<GridKVPersistence>,
+    task_scheduler: GridTaskSchedulerRwLock,
 }
 
 impl GridManager {
@@ -35,12 +40,14 @@ impl GridManager {
     ) -> Self {
         let grid_editors = Arc::new(DashMap::new());
         let kv_persistence = Arc::new(GridKVPersistence::new(database.clone()));
-        let block_index_persistence = Arc::new(BlockIndexCache::new(database));
+        let block_index_cache = Arc::new(BlockIndexCache::new(database));
+        let task_scheduler = GridTaskScheduler::new();
         Self {
-            editor_map: grid_editors,
+            grid_editors,
             grid_user,
             kv_persistence,
-            block_index_cache: block_index_persistence,
+            block_index_cache,
+            task_scheduler,
         }
     }
 
@@ -74,18 +81,20 @@ impl GridManager {
     }
 
     #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
-    pub fn close_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
+    pub async fn close_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
-        self.editor_map.remove(grid_id);
+        self.grid_editors.remove(grid_id);
+        self.task_scheduler.write().await.unregister_handler(grid_id);
         Ok(())
     }
 
     #[tracing::instrument(level = "debug", skip(self, grid_id), fields(doc_id), err)]
-    pub fn delete_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
+    pub async fn delete_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
-        self.editor_map.remove(grid_id);
+        self.grid_editors.remove(grid_id);
+        self.task_scheduler.write().await.unregister_handler(grid_id);
         Ok(())
     }
 
@@ -93,23 +102,24 @@ impl GridManager {
 
     // #[tracing::instrument(level = "debug", skip(self), err)]
     pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
-        match self.editor_map.get(grid_id) {
+        match self.grid_editors.get(grid_id) {
             None => Err(FlowyError::internal().context("Should call open_grid function first")),
             Some(editor) => Ok(editor.clone()),
         }
     }
 
     async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
-        match self.editor_map.get(grid_id) {
+        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_editor(grid_id, db_pool).await?;
 
-                if self.editor_map.contains_key(grid_id) {
+                if self.grid_editors.contains_key(grid_id) {
                     tracing::warn!("Grid:{} already exists in cache", grid_id);
                 }
-                self.editor_map.insert(grid_id.to_string(), editor.clone());
+                self.grid_editors.insert(grid_id.to_string(), editor.clone());
+                self.task_scheduler.write().await.register_handler(editor.clone());
                 Ok(editor)
             }
             Some(editor) => Ok(editor.clone()),
@@ -124,7 +134,14 @@ impl GridManager {
     ) -> Result<Arc<GridRevisionEditor>, FlowyError> {
         let user = self.grid_user.clone();
         let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
-        let grid_editor = GridRevisionEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
+        let grid_editor = GridRevisionEditor::new(
+            grid_id,
+            user,
+            rev_manager,
+            self.block_index_cache.clone(),
+            self.task_scheduler.clone(),
+        )
+        .await?;
         Ok(grid_editor)
     }
 
@@ -156,37 +173,31 @@ pub async fn make_grid_view_data(
     grid_manager: Arc<GridManager>,
     build_context: BuildGridContext,
 ) -> FlowyResult<Bytes> {
-    let grid_rev = GridRevision {
-        grid_id: view_id.to_string(),
-        fields: build_context.field_revs,
-        blocks: build_context.blocks,
-        setting: GridSettingRevision::default(),
-    };
-
-    // Create grid
-    let grid_meta_delta = make_grid_delta(&grid_rev);
-    let grid_delta_data = grid_meta_delta.to_delta_bytes();
-    let repeated_revision: RepeatedRevision =
-        Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
-    let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
-
-    for block_meta_data in build_context.blocks_meta_data {
-        let block_id = block_meta_data.block_id.clone();
-
+    for block_meta_data in &build_context.blocks_meta_data {
+        let block_id = &block_meta_data.block_id;
         // Indexing the block's rows
         block_meta_data.rows.iter().for_each(|row| {
             let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
         });
 
         // Create grid's block
-        let grid_block_meta_delta = make_block_meta_delta(&block_meta_data);
+        let grid_block_meta_delta = make_block_meta_delta(block_meta_data);
         let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
         let repeated_revision: RepeatedRevision =
-            Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into();
+            Revision::initial_revision(user_id, block_id, block_meta_delta_data).into();
         let _ = grid_manager
             .create_grid_block_meta(&block_id, repeated_revision)
             .await?;
     }
 
+    let grid_rev = GridRevision::from_build_context(view_id, build_context);
+
+    // Create grid
+    let grid_meta_delta = make_grid_delta(&grid_rev);
+    let grid_delta_data = grid_meta_delta.to_delta_bytes();
+    let repeated_revision: RepeatedRevision =
+        Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
+    let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
+
     Ok(grid_delta_data)
 }

+ 29 - 21
frontend/rust-lib/flowy-grid/src/services/block_manager.rs

@@ -2,14 +2,14 @@ use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::manager::GridUser;
 use crate::services::block_revision_editor::GridBlockRevisionEditor;
 use crate::services::persistence::block_index::BlockIndexCache;
-use crate::services::row::{group_row_orders, GridBlockSnapshot};
+use crate::services::row::{block_from_row_orders, GridBlockSnapshot};
 use dashmap::DashMap;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::entities::{
     CellChangeset, GridRowsChangeset, IndexRowOrder, Row, RowOrder, UpdatedRowOrder,
 };
 use flowy_grid_data_model::revision::{
-    CellRevision, GridBlockRevision, GridBlockRevisionChangeset, RowMetaChangeset, RowRevision,
+    CellRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
 };
 use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
 use flowy_revision::{RevisionManager, RevisionPersistence};
@@ -19,6 +19,7 @@ use std::sync::Arc;
 
 type BlockId = String;
 pub(crate) struct GridBlockManager {
+    #[allow(dead_code)]
     grid_id: String,
     user: Arc<dyn GridUser>,
     persistence: Arc<BlockIndexCache>,
@@ -29,10 +30,10 @@ impl GridBlockManager {
     pub(crate) async fn new(
         grid_id: &str,
         user: &Arc<dyn GridUser>,
-        blocks: Vec<GridBlockRevision>,
+        block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
         persistence: Arc<BlockIndexCache>,
     ) -> FlowyResult<Self> {
-        let editor_map = make_block_meta_editor_map(user, blocks).await?;
+        let editor_map = make_block_meta_editor_map(user, block_meta_revs).await?;
         let user = user.clone();
         let grid_id = grid_id.to_owned();
         let manager = Self {
@@ -77,7 +78,7 @@ impl GridBlockManager {
         index_row_order.index = row_index;
 
         let _ = self
-            .notify_did_update_block(GridRowsChangeset::insert(block_id, vec![index_row_order]))
+            .notify_did_update_block(block_id, GridRowsChangeset::insert(block_id, vec![index_row_order]))
             .await?;
         Ok(row_count)
     }
@@ -85,7 +86,7 @@ impl GridBlockManager {
     pub(crate) async fn insert_row(
         &self,
         rows_by_block_id: HashMap<String, Vec<RowRevision>>,
-    ) -> FlowyResult<Vec<GridBlockRevisionChangeset>> {
+    ) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
         let mut changesets = vec![];
         for (block_id, row_revs) in rows_by_block_id {
             let mut inserted_row_orders = vec![];
@@ -99,10 +100,10 @@ impl GridBlockManager {
                 row_order.index = index;
                 inserted_row_orders.push(row_order);
             }
-            changesets.push(GridBlockRevisionChangeset::from_row_count(&block_id, row_count));
+            changesets.push(GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count));
 
             let _ = self
-                .notify_did_update_block(GridRowsChangeset::insert(&block_id, inserted_row_orders))
+                .notify_did_update_block(&block_id, GridRowsChangeset::insert(&block_id, inserted_row_orders))
                 .await?;
         }
 
@@ -121,7 +122,9 @@ impl GridBlockManager {
                 if let Some(row) = row_builder(row_rev.clone()) {
                     let row_order = UpdatedRowOrder::new(&row_rev, row);
                     let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]);
-                    let _ = self.notify_did_update_block(block_order_changeset).await?;
+                    let _ = self
+                        .notify_did_update_block(&editor.block_id, block_order_changeset)
+                        .await?;
                 }
             }
         }
@@ -137,7 +140,7 @@ impl GridBlockManager {
             Some(row_order) => {
                 let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
                 let _ = self
-                    .notify_did_update_block(GridRowsChangeset::delete(&block_id, vec![row_order]))
+                    .notify_did_update_block(&block_id, GridRowsChangeset::delete(&block_id, vec![row_order]))
                     .await?;
             }
         }
@@ -145,17 +148,20 @@ impl GridBlockManager {
         Ok(())
     }
 
-    pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockRevisionChangeset>> {
+    pub(crate) async fn delete_rows(
+        &self,
+        row_orders: Vec<RowOrder>,
+    ) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
         let mut changesets = vec![];
-        for block_order in group_row_orders(row_orders) {
-            let editor = self.get_editor(&block_order.block_id).await?;
+        for block_order in block_from_row_orders(row_orders) {
+            let editor = self.get_editor(&block_order.id).await?;
             let row_ids = block_order
                 .row_orders
                 .into_iter()
                 .map(|row_order| Cow::Owned(row_order.row_id))
                 .collect::<Vec<Cow<String>>>();
             let row_count = editor.delete_rows(row_ids).await?;
-            let changeset = GridBlockRevisionChangeset::from_row_count(&block_order.block_id, row_count);
+            let changeset = GridBlockMetaRevisionChangeset::from_row_count(&block_order.id, row_count);
             changesets.push(changeset);
         }
 
@@ -181,7 +187,9 @@ impl GridBlockManager {
                     updated_rows: vec![],
                 };
 
-                let _ = self.notify_did_update_block(notified_changeset).await?;
+                let _ = self
+                    .notify_did_update_block(&editor.block_id, notified_changeset)
+                    .await?;
             }
         }
 
@@ -241,8 +249,8 @@ impl GridBlockManager {
         Ok(block_cell_revs)
     }
 
-    async fn notify_did_update_block(&self, changeset: GridRowsChangeset) -> FlowyResult<()> {
-        send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridRow)
+    async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> {
+        send_dart_notification(block_id, GridNotification::DidUpdateGridBlock)
             .payload(changeset)
             .send();
         Ok(())
@@ -257,12 +265,12 @@ impl GridBlockManager {
 
 async fn make_block_meta_editor_map(
     user: &Arc<dyn GridUser>,
-    blocks: Vec<GridBlockRevision>,
+    block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
 ) -> FlowyResult<DashMap<String, Arc<GridBlockRevisionEditor>>> {
     let editor_map = DashMap::new();
-    for block in blocks {
-        let editor = make_block_meta_editor(user, &block.block_id).await?;
-        editor_map.insert(block.block_id, Arc::new(editor));
+    for block_meta_rev in block_meta_revs {
+        let editor = make_block_meta_editor(user, &block_meta_rev.block_id).await?;
+        editor_map.insert(block_meta_rev.block_id.clone(), Arc::new(editor));
     }
 
     Ok(editor_map)

+ 6 - 6
frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs

@@ -1,7 +1,7 @@
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::RowOrder;
-use flowy_grid_data_model::revision::{CellRevision, GridBlockRevisionData, RowMetaChangeset, RowRevision};
+use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision};
 use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
 use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockRevisionPad};
 use flowy_sync::entities::revision::Revision;
@@ -42,28 +42,28 @@ impl GridBlockRevisionEditor {
         })
     }
 
-    pub async fn duplicate_block_meta_data(&self, duplicated_block_id: &str) -> GridBlockRevisionData {
+    pub async fn duplicate_block(&self, duplicated_block_id: &str) -> GridBlockRevision {
         self.pad.read().await.duplicate_data(duplicated_block_id).await
     }
 
-    /// return current number of rows and the inserted index. The inserted index will be None if the start_row_id is None
+    /// Create a row after the the with prev_row_id. If prev_row_id is None, the row will be appended to the list
     pub(crate) async fn create_row(
         &self,
         row: RowRevision,
-        start_row_id: Option<String>,
+        prev_row_id: Option<String>,
     ) -> FlowyResult<(i32, Option<i32>)> {
         let mut row_count = 0;
         let mut row_index = None;
         let _ = self
             .modify(|block_pad| {
-                if let Some(start_row_id) = start_row_id.as_ref() {
+                if let Some(start_row_id) = prev_row_id.as_ref() {
                     match block_pad.index_of_row(start_row_id) {
                         None => {}
                         Some(index) => row_index = Some(index + 1),
                     }
                 }
 
-                let change = block_pad.add_row_rev(row, start_row_id)?;
+                let change = block_pad.add_row_rev(row, prev_row_id)?;
                 row_count = block_pad.number_of_rows();
 
                 if row_index.is_none() {

+ 7 - 25
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs

@@ -93,7 +93,7 @@ mod tests {
     use crate::services::field::type_options::checkbox_type_option::{NO, YES};
 
     use crate::services::field::FieldBuilder;
-    use crate::services::row::{apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data};
+    use crate::services::row::{apply_cell_data_changeset, decode_cell_data};
 
     use flowy_grid_data_model::entities::FieldType;
 
@@ -101,39 +101,21 @@ mod tests {
     fn checkout_box_description_test() {
         let field_rev = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
         let data = apply_cell_data_changeset("true", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            YES
-        );
+        assert_eq!(decode_cell_data(data, &field_rev).to_string(), YES);
 
         let data = apply_cell_data_changeset("1", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            YES
-        );
+        assert_eq!(decode_cell_data(data, &field_rev,).to_string(), YES);
 
         let data = apply_cell_data_changeset("yes", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            YES
-        );
+        assert_eq!(decode_cell_data(data, &field_rev,).to_string(), YES);
 
         let data = apply_cell_data_changeset("false", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            NO
-        );
+        assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
 
         let data = apply_cell_data_changeset("no", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            NO
-        );
+        assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
 
         let data = apply_cell_data_changeset("12", None, &field_rev).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_rev, &field_rev.field_type).to_string(),
-            NO
-        );
+        assert_eq!(decode_cell_data(data, &field_rev,).to_string(), NO);
     }
 }

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -1,6 +1,6 @@
 use crate::impl_type_option;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{decode_cell_data, CellContentChangeset, CellDataOperation, DecodedCellData};
+use crate::services::row::{try_decode_cell_data, CellContentChangeset, CellDataOperation, DecodedCellData};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -45,7 +45,7 @@ impl CellDataOperation<String> for RichTextTypeOption {
             || decoded_field_type.is_multi_select()
             || decoded_field_type.is_number()
         {
-            decode_cell_data(encoded_data, decoded_field_type, decoded_field_type, field_rev)
+            try_decode_cell_data(encoded_data, field_rev, decoded_field_type, decoded_field_type)
         } else {
             let cell_data = encoded_data.into();
             Ok(DecodedCellData::new(cell_data))

+ 58 - 0
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -0,0 +1,58 @@
+use crate::manager::GridTaskSchedulerRwLock;
+use crate::services::block_manager::GridBlockManager;
+use crate::services::tasks::Task;
+use flowy_error::FlowyResult;
+
+use flowy_sync::client_grid::GridRevisionPad;
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+pub(crate) struct GridFilterService {
+    #[allow(dead_code)]
+    scheduler: GridTaskSchedulerRwLock,
+    #[allow(dead_code)]
+    grid_pad: Arc<RwLock<GridRevisionPad>>,
+    #[allow(dead_code)]
+    block_manager: Arc<GridBlockManager>,
+}
+impl GridFilterService {
+    pub fn new(
+        grid_pad: Arc<RwLock<GridRevisionPad>>,
+        block_manager: Arc<GridBlockManager>,
+        scheduler: GridTaskSchedulerRwLock,
+    ) -> Self {
+        Self {
+            grid_pad,
+            block_manager,
+            scheduler,
+        }
+    }
+
+    pub async fn process_task(&self, _task: Task) -> FlowyResult<()> {
+        Ok(())
+    }
+
+    pub async fn notify_changed(&self) {
+        //
+        // let grid_pad = self.grid_pad.read().await;
+        // match grid_pad.get_filters(None) {
+        //     None => {}
+        //     Some(filter_revs) => {
+        //         filter_revs
+        //             .iter()
+        //             .for_each(|filter_rev| match grid_pad.get_field_rev(&filter_rev.field_id) {
+        //                 None => {}
+        //                 Some((_, _field_rev)) => match field_rev.field_type {
+        //                     FieldType::RichText => {}
+        //                     FieldType::Number => {}
+        //                     FieldType::DateTime => {}
+        //                     FieldType::SingleSelect => {}
+        //                     FieldType::MultiSelect => {}
+        //                     FieldType::Checkbox => {}
+        //                     FieldType::URL => {}
+        //                 },
+        //             });
+        //     }
+        // }
+    }
+}

+ 3 - 0
frontend/rust-lib/flowy-grid/src/services/filter/mod.rs

@@ -0,0 +1,3 @@
+mod filter_service;
+
+pub(crate) use filter_service::*;

+ 62 - 35
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -1,10 +1,12 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::CellIdentifier;
-use crate::manager::GridUser;
+use crate::manager::{GridTaskSchedulerRwLock, GridUser};
 use crate::services::block_manager::GridBlockManager;
 use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
+use crate::services::filter::GridFilterService;
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::row::*;
+
 use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
@@ -21,16 +23,18 @@ use std::sync::Arc;
 use tokio::sync::RwLock;
 
 pub struct GridRevisionEditor {
-    grid_id: String,
+    pub(crate) grid_id: String,
     user: Arc<dyn GridUser>,
     grid_pad: Arc<RwLock<GridRevisionPad>>,
     rev_manager: Arc<RevisionManager>,
     block_manager: Arc<GridBlockManager>,
+    #[allow(dead_code)]
+    pub(crate) filter_service: Arc<GridFilterService>,
 }
 
 impl Drop for GridRevisionEditor {
     fn drop(&mut self) {
-        tracing::trace!("Drop GridMetaEditor");
+        tracing::trace!("Drop GridRevisionEditor");
     }
 }
 
@@ -40,22 +44,30 @@ impl GridRevisionEditor {
         user: Arc<dyn GridUser>,
         mut rev_manager: RevisionManager,
         persistence: Arc<BlockIndexCache>,
+        task_scheduler: GridTaskSchedulerRwLock,
     ) -> FlowyResult<Arc<Self>> {
         let token = user.token()?;
         let cloud = Arc::new(GridRevisionCloudService { token });
         let grid_pad = rev_manager.load::<GridPadBuilder>(Some(cloud)).await?;
         let rev_manager = Arc::new(rev_manager);
         let grid_pad = Arc::new(RwLock::new(grid_pad));
-        let blocks = grid_pad.read().await.get_block_revs();
-
-        let block_meta_manager = Arc::new(GridBlockManager::new(grid_id, &user, blocks, persistence).await?);
-        Ok(Arc::new(Self {
+        let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
+        let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
+        let filter_service = Arc::new(GridFilterService::new(
+            grid_pad.clone(),
+            block_manager.clone(),
+            task_scheduler.clone(),
+        ));
+        let editor = Arc::new(Self {
             grid_id: grid_id.to_owned(),
             user,
             grid_pad,
             rev_manager,
-            block_manager: block_meta_manager,
-        }))
+            block_manager,
+            filter_service,
+        });
+
+        Ok(editor)
     }
 
     pub async fn insert_field(&self, params: InsertFieldParams) -> FlowyResult<()> {
@@ -243,14 +255,14 @@ impl GridRevisionEditor {
         Ok(field_revs)
     }
 
-    pub async fn create_block(&self, grid_block: GridBlockRevision) -> FlowyResult<()> {
+    pub async fn create_block(&self, block_meta_rev: GridBlockMetaRevision) -> FlowyResult<()> {
         let _ = self
-            .modify(|grid_pad| Ok(grid_pad.create_block_rev(grid_block)?))
+            .modify(|grid_pad| Ok(grid_pad.create_block_meta_rev(block_meta_rev)?))
             .await?;
         Ok(())
     }
 
-    pub async fn update_block(&self, changeset: GridBlockRevisionChangeset) -> FlowyResult<()> {
+    pub async fn update_block(&self, changeset: GridBlockMetaRevisionChangeset) -> FlowyResult<()> {
         let _ = self
             .modify(|grid_pad| Ok(grid_pad.update_block_rev(changeset)?))
             .await?;
@@ -270,7 +282,7 @@ impl GridRevisionEditor {
         let row_count = self.block_manager.create_row(&block_id, row_rev, start_row_id).await?;
 
         // update block row count
-        let changeset = GridBlockRevisionChangeset::from_row_count(&block_id, row_count);
+        let changeset = GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count);
         let _ = self.update_block(changeset).await?;
         Ok(row_order)
     }
@@ -342,7 +354,10 @@ impl GridRevisionEditor {
     pub async fn get_cell(&self, params: &CellIdentifier) -> Option<Cell> {
         let field_rev = self.get_field_rev(&params.field_id).await?;
         let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
-        make_cell(&params.field_id, &field_rev, &row_rev)
+
+        let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
+        let data = decode_cell_data(cell_rev.data, &field_rev).data;
+        Some(Cell::new(&params.field_id, data))
     }
 
     pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellRevision>> {
@@ -405,9 +420,9 @@ impl GridRevisionEditor {
         make_grid_blocks(block_ids, block_snapshots)
     }
 
-    pub async fn get_block_metas(&self) -> FlowyResult<Vec<GridBlockRevision>> {
-        let grid_blocks = self.grid_pad.read().await.get_block_revs();
-        Ok(grid_blocks)
+    pub async fn get_block_meta_revs(&self) -> FlowyResult<Vec<Arc<GridBlockMetaRevision>>> {
+        let block_meta_revs = self.grid_pad.read().await.get_block_meta_revs();
+        Ok(block_meta_revs)
     }
 
     pub async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<()> {
@@ -422,10 +437,10 @@ impl GridRevisionEditor {
         let pad_read_guard = self.grid_pad.read().await;
         let field_orders = pad_read_guard.get_field_orders();
         let mut block_orders = vec![];
-        for block_order in pad_read_guard.get_block_revs() {
-            let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?;
-            let block_order = GridBlockOrder {
-                block_id: block_order.block_id,
+        for block_rev in pad_read_guard.get_block_meta_revs() {
+            let row_orders = self.block_manager.get_row_orders(&block_rev.block_id).await?;
+            let block_order = GridBlock {
+                id: block_rev.block_id.clone(),
                 row_orders,
             };
             block_orders.push(block_order);
@@ -434,20 +449,34 @@ impl GridRevisionEditor {
         Ok(Grid {
             id: self.grid_id.clone(),
             field_orders,
-            block_orders,
+            blocks: block_orders,
         })
     }
 
-    pub async fn get_grid_setting(&self) -> FlowyResult<GridSettingRevision> {
-        let pad_read_guard = self.grid_pad.read().await;
-        let grid_setting_rev = pad_read_guard.get_grid_setting_rev();
-        Ok(grid_setting_rev)
+    pub async fn get_grid_setting(&self) -> FlowyResult<GridSetting> {
+        let read_guard = self.grid_pad.read().await;
+        let grid_setting_rev = read_guard.get_grid_setting_rev();
+        Ok(grid_setting_rev.into())
+    }
+
+    pub async fn get_grid_filter(&self, layout_type: &GridLayoutType) -> FlowyResult<Vec<GridFilter>> {
+        let read_guard = self.grid_pad.read().await;
+        let layout_rev = layout_type.clone().into();
+        match read_guard.get_filters(Some(&layout_rev)) {
+            Some(filter_revs) => Ok(filter_revs.iter().map(GridFilter::from).collect::<Vec<GridFilter>>()),
+            None => Ok(vec![]),
+        }
     }
 
     pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
+        let is_filter_changed = params.is_filter_changed();
         let _ = self
             .modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
             .await?;
+
+        if is_filter_changed {
+            self.filter_service.notify_changed().await;
+        }
         Ok(())
     }
 
@@ -457,9 +486,9 @@ impl GridRevisionEditor {
                 .grid_pad
                 .read()
                 .await
-                .get_block_revs()
-                .into_iter()
-                .map(|block_meta| block_meta.block_id)
+                .get_block_meta_revs()
+                .iter()
+                .map(|block_rev| block_rev.block_id.clone())
                 .collect::<Vec<String>>(),
             Some(block_ids) => block_ids,
         };
@@ -507,8 +536,8 @@ impl GridRevisionEditor {
 
     pub async fn duplicate_grid(&self) -> FlowyResult<BuildGridContext> {
         let grid_pad = self.grid_pad.read().await;
-        let original_blocks = grid_pad.get_block_revs();
-        let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_meta().await;
+        let original_blocks = grid_pad.get_block_meta_revs();
+        let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_block_meta().await;
 
         let mut blocks_meta_data = vec![];
         if original_blocks.len() == duplicated_blocks.len() {
@@ -517,9 +546,7 @@ impl GridRevisionEditor {
                 let duplicated_block_id = &duplicated_blocks[index].block_id;
 
                 tracing::trace!("Duplicate block:{} meta data", duplicated_block_id);
-                let duplicated_block_meta_data = grid_block_meta_editor
-                    .duplicate_block_meta_data(duplicated_block_id)
-                    .await;
+                let duplicated_block_meta_data = grid_block_meta_editor.duplicate_block(duplicated_block_id).await;
                 blocks_meta_data.push(duplicated_block_meta_data);
             }
         } else {
@@ -566,7 +593,7 @@ impl GridRevisionEditor {
     }
 
     async fn block_id(&self) -> FlowyResult<String> {
-        match self.grid_pad.read().await.get_block_revs().last() {
+        match self.grid_pad.read().await.get_block_meta_revs().last() {
             None => Err(FlowyError::internal().context("There is no grid block in this grid")),
             Some(grid_block) => Ok(grid_block.block_id.clone()),
         }

+ 21 - 0
frontend/rust-lib/flowy-grid/src/services/grid_editor_task.rs

@@ -0,0 +1,21 @@
+use crate::services::grid_editor::GridRevisionEditor;
+use crate::services::tasks::{GridTaskHandler, Task, TaskContent, TaskHandlerId};
+use flowy_error::FlowyError;
+
+use lib_infra::future::BoxResultFuture;
+
+impl GridTaskHandler for GridRevisionEditor {
+    fn handler_id(&self) -> &TaskHandlerId {
+        &self.grid_id
+    }
+
+    fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError> {
+        Box::pin(async move {
+            match &task.content {
+                TaskContent::Snapshot { .. } => {}
+                TaskContent::Filter => self.filter_service.process_task(task).await?,
+            }
+            Ok(())
+        })
+    }
+}

+ 5 - 0
frontend/rust-lib/flowy-grid/src/services/mod.rs

@@ -3,6 +3,11 @@ mod util;
 mod block_manager;
 pub mod block_revision_editor;
 pub mod field;
+mod filter;
 pub mod grid_editor;
+mod grid_editor_task;
 pub mod persistence;
 pub mod row;
+pub mod setting;
+mod snapshot;
+pub mod tasks;

+ 14 - 15
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -55,12 +55,6 @@ pub struct TypeOptionCellData {
     pub field_type: FieldType,
 }
 
-impl TypeOptionCellData {
-    pub fn split(self) -> (String, FieldType) {
-        (self.data, self.field_type)
-    }
-}
-
 impl std::str::FromStr for TypeOptionCellData {
     type Err = FlowyError;
 
@@ -139,14 +133,11 @@ pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>(
     Ok(TypeOptionCellData::new(s, field_rev.field_type.clone()).json())
 }
 
-pub fn decode_cell_data_from_type_option_cell_data<T: TryInto<TypeOptionCellData>>(
-    data: T,
-    field_rev: &FieldRevision,
-    field_type: &FieldType,
-) -> DecodedCellData {
+pub fn decode_cell_data<T: TryInto<TypeOptionCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
     if let Ok(type_option_cell_data) = data.try_into() {
-        let (encoded_data, s_field_type) = type_option_cell_data.split();
-        match decode_cell_data(encoded_data, &s_field_type, field_type, field_rev) {
+        let TypeOptionCellData { data, field_type } = type_option_cell_data;
+        let to_field_type = &field_rev.field_type;
+        match try_decode_cell_data(data, field_rev, &field_type, to_field_type) {
             Ok(cell_data) => cell_data,
             Err(e) => {
                 tracing::error!("Decode cell data failed, {:?}", e);
@@ -159,11 +150,11 @@ pub fn decode_cell_data_from_type_option_cell_data<T: TryInto<TypeOptionCellData
     }
 }
 
-pub fn decode_cell_data<T: Into<String>>(
+pub fn try_decode_cell_data<T: Into<String>>(
     encoded_data: T,
+    field_rev: &FieldRevision,
     s_field_type: &FieldType,
     t_field_type: &FieldType,
-    field_rev: &FieldRevision,
 ) -> FlowyResult<DecodedCellData> {
     let encoded_data = encoded_data.into();
     let get_cell_data = || {
@@ -229,6 +220,14 @@ where
     }
 }
 
+/// The data is encoded by protobuf or utf8. You should choose the corresponding decode struct to parse it.
+///
+/// For example:
+///
+/// * Use DateCellData to parse the data when the FieldType is Date.
+/// * Use URLCellData to parse the data when the FieldType is URL.
+/// * Use String to parse the data when the FieldType is RichText, Number, or Checkbox.
+/// * Check out the implementation of CellDataOperation trait for more information.
 #[derive(Default)]
 pub struct DecodedCellData {
     pub data: Vec<u8>,

+ 28 - 36
frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs

@@ -1,7 +1,6 @@
-use crate::services::row::decode_cell_data_from_type_option_cell_data;
 use flowy_error::FlowyResult;
-use flowy_grid_data_model::entities::{Cell, GridBlock, GridBlockOrder, RepeatedGridBlock, Row, RowOrder};
-use flowy_grid_data_model::revision::{CellRevision, FieldRevision, RowRevision};
+use flowy_grid_data_model::entities::{GridBlock, RepeatedGridBlock, Row, RowOrder};
+use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
 use std::collections::HashMap;
 use std::sync::Arc;
 
@@ -10,36 +9,30 @@ pub struct GridBlockSnapshot {
     pub row_revs: Vec<Arc<RowRevision>>,
 }
 
-pub(crate) fn group_row_orders(row_orders: Vec<RowOrder>) -> Vec<GridBlockOrder> {
-    let mut map: HashMap<String, GridBlockOrder> = HashMap::new();
+pub(crate) fn block_from_row_orders(row_orders: Vec<RowOrder>) -> Vec<GridBlock> {
+    let mut map: HashMap<String, GridBlock> = HashMap::new();
     row_orders.into_iter().for_each(|row_order| {
         // Memory Optimization: escape clone block_id
         let block_id = row_order.block_id.clone();
         map.entry(block_id)
-            .or_insert_with(|| GridBlockOrder::new(&row_order.block_id))
+            .or_insert_with(|| GridBlock::new(&row_order.block_id, vec![]))
             .row_orders
             .push(row_order);
     });
     map.into_values().collect::<Vec<_>>()
 }
-
-#[inline(always)]
-pub fn make_cell_by_field_id(
-    field_map: &HashMap<&String, &FieldRevision>,
-    field_id: String,
-    cell_rev: CellRevision,
-) -> Option<(String, Cell)> {
-    let field_rev = field_map.get(&field_id)?;
-    let data = decode_cell_data_from_type_option_cell_data(cell_rev.data, field_rev, &field_rev.field_type).data;
-    let cell = Cell::new(&field_id, data);
-    Some((field_id, cell))
-}
-
-pub fn make_cell(field_id: &str, field_rev: &FieldRevision, row_rev: &RowRevision) -> Option<Cell> {
-    let cell_rev = row_rev.cells.get(field_id)?.clone();
-    let data = decode_cell_data_from_type_option_cell_data(cell_rev.data, field_rev, &field_rev.field_type).data;
-    Some(Cell::new(field_id, data))
-}
+//
+// #[inline(always)]
+// fn make_cell_by_field_id(
+//     field_map: &HashMap<&String, &FieldRevision>,
+//     field_id: String,
+//     cell_rev: CellRevision,
+// ) -> Option<(String, Cell)> {
+//     let field_rev = field_map.get(&field_id)?;
+//     let data = decode_cell_data(cell_rev.data, field_rev).data;
+//     let cell = Cell::new(&field_id, data);
+//     Some((field_id, cell))
+// }
 
 pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<RowOrder> {
     row_revs.iter().map(RowOrder::from).collect::<Vec<_>>()
@@ -49,23 +42,22 @@ pub(crate) fn make_row_from_row_rev(fields: &[FieldRevision], row_rev: Arc<RowRe
     make_rows_from_row_revs(fields, &[row_rev]).pop()
 }
 
-pub(crate) fn make_rows_from_row_revs(fields: &[FieldRevision], row_revs: &[Arc<RowRevision>]) -> Vec<Row> {
-    let field_rev_map = fields
-        .iter()
-        .map(|field_rev| (&field_rev.id, field_rev))
-        .collect::<HashMap<&String, &FieldRevision>>();
+pub(crate) fn make_rows_from_row_revs(_fields: &[FieldRevision], row_revs: &[Arc<RowRevision>]) -> Vec<Row> {
+    // let field_rev_map = fields
+    //     .iter()
+    //     .map(|field_rev| (&field_rev.id, field_rev))
+    //     .collect::<HashMap<&String, &FieldRevision>>();
 
     let make_row = |row_rev: &Arc<RowRevision>| {
-        let cell_by_field_id = row_rev
-            .cells
-            .clone()
-            .into_iter()
-            .flat_map(|(field_id, cell_rev)| make_cell_by_field_id(&field_rev_map, field_id, cell_rev))
-            .collect::<HashMap<String, Cell>>();
+        // let cell_by_field_id = row_rev
+        //     .cells
+        //     .clone()
+        //     .into_iter()
+        //     .flat_map(|(field_id, cell_rev)| make_cell_by_field_id(&field_rev_map, field_id, cell_rev))
+        //     .collect::<HashMap<String, Cell>>();
 
         Row {
             id: row_rev.id.clone(),
-            cell_by_field_id,
             height: row_rev.height,
         }
     };

+ 3 - 0
frontend/rust-lib/flowy-grid/src/services/setting/mod.rs

@@ -0,0 +1,3 @@
+mod setting_builder;
+
+pub use setting_builder::*;

+ 35 - 0
frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs

@@ -0,0 +1,35 @@
+use flowy_grid_data_model::entities::{CreateGridFilterParams, GridLayoutType, GridSettingChangesetParams};
+
+pub struct GridSettingChangesetBuilder {
+    params: GridSettingChangesetParams,
+}
+
+impl GridSettingChangesetBuilder {
+    pub fn new(grid_id: &str, layout_type: &GridLayoutType) -> Self {
+        let params = GridSettingChangesetParams {
+            grid_id: grid_id.to_string(),
+            layout_type: layout_type.clone(),
+            insert_filter: None,
+            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 {
+        self.params.insert_filter = Some(params);
+        self
+    }
+
+    pub fn delete_filter(mut self, filter_id: &str) -> Self {
+        self.params.delete_filter = Some(filter_id.to_string());
+        self
+    }
+
+    pub fn build(self) -> GridSettingChangesetParams {
+        self.params
+    }
+}

+ 3 - 0
frontend/rust-lib/flowy-grid/src/services/snapshot/mod.rs

@@ -0,0 +1,3 @@
+mod snapshot_service;
+
+pub use snapshot_service::*;

+ 7 - 0
frontend/rust-lib/flowy-grid/src/services/snapshot/snapshot_service.rs

@@ -0,0 +1,7 @@
+// pub struct GridSnapshotService {}
+//
+// impl GridSnapshotService {
+//     pub fn new() -> Self {
+//         Self {}
+//     }
+// }

+ 9 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/mod.rs

@@ -0,0 +1,9 @@
+mod queue;
+mod runner;
+mod scheduler;
+mod store;
+mod task;
+
+pub use queue::TaskHandlerId;
+pub use scheduler::*;
+pub use task::*;

+ 119 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/queue.rs

@@ -0,0 +1,119 @@
+use crate::services::tasks::task::{PendingTask, Task, TaskContent, TaskType};
+use atomic_refcell::AtomicRefCell;
+
+use std::cmp::Ordering;
+use std::collections::hash_map::Entry;
+use std::collections::{BinaryHeap, HashMap};
+use std::ops::{Deref, DerefMut};
+use std::sync::Arc;
+
+#[derive(Default)]
+pub(crate) struct GridTaskQueue {
+    // index_tasks for quick access
+    index_tasks: HashMap<TaskHandlerId, Arc<AtomicRefCell<TaskList>>>,
+    queue: BinaryHeap<Arc<AtomicRefCell<TaskList>>>,
+}
+
+impl GridTaskQueue {
+    pub(crate) fn new() -> Self {
+        Self::default()
+    }
+
+    pub(crate) fn push(&mut self, task: &Task) {
+        let task_type = match task.content {
+            TaskContent::Snapshot { .. } => TaskType::Snapshot,
+            TaskContent::Filter => TaskType::Filter,
+        };
+        let pending_task = PendingTask {
+            ty: task_type,
+            id: task.id,
+        };
+        match self.index_tasks.entry("1".to_owned()) {
+            Entry::Occupied(entry) => {
+                let mut list = entry.get().borrow_mut();
+                assert!(list.peek().map(|old_id| pending_task.id >= old_id.id).unwrap_or(true));
+                list.push(pending_task);
+            }
+            Entry::Vacant(entry) => {
+                let mut task_list = TaskList::new(entry.key());
+                task_list.push(pending_task);
+                let task_list = Arc::new(AtomicRefCell::new(task_list));
+                entry.insert(task_list.clone());
+                self.queue.push(task_list);
+            }
+        }
+    }
+
+    pub(crate) fn mut_head<T, F>(&mut self, mut f: F) -> Option<T>
+    where
+        F: FnMut(&mut TaskList) -> Option<T>,
+    {
+        let head = self.queue.pop()?;
+        let result = {
+            let mut ref_head = head.borrow_mut();
+            f(&mut *ref_head)
+        };
+        if !head.borrow().tasks.is_empty() {
+            self.queue.push(head);
+        } else {
+            self.index_tasks.remove(&head.borrow().id);
+        }
+        result
+    }
+}
+
+pub type TaskHandlerId = String;
+
+#[derive(Debug)]
+pub(crate) struct TaskList {
+    pub(crate) id: TaskHandlerId,
+    tasks: BinaryHeap<PendingTask>,
+}
+
+impl Deref for TaskList {
+    type Target = BinaryHeap<PendingTask>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.tasks
+    }
+}
+
+impl DerefMut for TaskList {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.tasks
+    }
+}
+
+impl TaskList {
+    fn new(id: &str) -> Self {
+        Self {
+            id: id.to_owned(),
+            tasks: BinaryHeap::new(),
+        }
+    }
+}
+
+impl PartialEq for TaskList {
+    fn eq(&self, other: &Self) -> bool {
+        self.id == other.id
+    }
+}
+
+impl Eq for TaskList {}
+
+impl Ord for TaskList {
+    fn cmp(&self, other: &Self) -> Ordering {
+        match (self.peek(), other.peek()) {
+            (None, None) => Ordering::Equal,
+            (None, Some(_)) => Ordering::Less,
+            (Some(_), None) => Ordering::Greater,
+            (Some(lhs), Some(rhs)) => lhs.cmp(rhs),
+        }
+    }
+}
+
+impl PartialOrd for TaskList {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}

+ 45 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/runner.rs

@@ -0,0 +1,45 @@
+use crate::services::tasks::scheduler::GridTaskScheduler;
+use std::sync::Arc;
+use std::time::Duration;
+use tokio::sync::{watch, RwLock};
+use tokio::time::interval;
+
+pub struct GridTaskRunner {
+    scheduler: Arc<RwLock<GridTaskScheduler>>,
+    debounce_duration: Duration,
+    notifier: Option<watch::Receiver<()>>,
+}
+
+impl GridTaskRunner {
+    pub fn new(
+        scheduler: Arc<RwLock<GridTaskScheduler>>,
+        notifier: watch::Receiver<()>,
+        debounce_duration: Duration,
+    ) -> Self {
+        Self {
+            scheduler,
+            debounce_duration,
+            notifier: Some(notifier),
+        }
+    }
+
+    pub async fn run(mut self) {
+        let mut notifier = self
+            .notifier
+            .take()
+            .expect("The GridTaskRunner's notifier should only take once");
+
+        loop {
+            if notifier.changed().await.is_err() {
+                // The runner will be stopped if the corresponding Sender drop.
+                break;
+            }
+            let mut interval = interval(self.debounce_duration);
+            interval.tick().await;
+
+            if let Err(e) = self.scheduler.write().await.process_next_task().await {
+                tracing::error!("{:?}", e);
+            }
+        }
+    }
+}

+ 87 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/scheduler.rs

@@ -0,0 +1,87 @@
+use crate::services::tasks::queue::{GridTaskQueue, TaskHandlerId};
+use crate::services::tasks::runner::GridTaskRunner;
+use crate::services::tasks::store::GridTaskStore;
+use crate::services::tasks::task::Task;
+
+use flowy_error::{FlowyError, FlowyResult};
+use lib_infra::future::BoxResultFuture;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::time::Duration;
+use tokio::sync::{watch, RwLock};
+
+pub trait GridTaskHandler: Send + Sync + 'static {
+    fn handler_id(&self) -> &TaskHandlerId;
+
+    fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError>;
+}
+
+pub struct GridTaskScheduler {
+    queue: GridTaskQueue,
+    store: GridTaskStore,
+    notifier: watch::Sender<()>,
+    handlers: HashMap<TaskHandlerId, Arc<dyn GridTaskHandler>>,
+}
+
+impl GridTaskScheduler {
+    pub fn new() -> Arc<RwLock<Self>> {
+        let (notifier, rx) = watch::channel(());
+
+        let scheduler = Self {
+            queue: GridTaskQueue::new(),
+            store: GridTaskStore::new(),
+            notifier,
+            handlers: HashMap::new(),
+        };
+        // The runner will receive the newest value after start running.
+        scheduler.notify();
+
+        let scheduler = Arc::new(RwLock::new(scheduler));
+        let debounce_duration = Duration::from_millis(300);
+        let runner = GridTaskRunner::new(scheduler.clone(), rx, debounce_duration);
+        tokio::spawn(runner.run());
+
+        scheduler
+    }
+
+    pub fn register_handler<T>(&mut self, handler: Arc<T>)
+    where
+        T: GridTaskHandler,
+    {
+        let handler_id = handler.handler_id().to_owned();
+        self.handlers.insert(handler_id, handler);
+    }
+
+    pub fn unregister_handler<T: AsRef<str>>(&mut self, handler_id: T) {
+        let _ = self.handlers.remove(handler_id.as_ref());
+    }
+
+    pub async fn process_next_task(&mut self) -> FlowyResult<()> {
+        let mut get_next_task = || {
+            let pending_task = self.queue.mut_head(|list| list.pop())?;
+            let task = self.store.remove_task(&pending_task.id)?;
+            Some(task)
+        };
+
+        if let Some(task) = get_next_task() {
+            match self.handlers.get(&task.hid) {
+                None => {}
+                Some(handler) => {
+                    let _ = handler.process_task(task).await;
+                }
+            }
+        }
+        Ok(())
+    }
+
+    pub fn register_task(&mut self, task: Task) {
+        assert!(!task.is_finished());
+        self.queue.push(&task);
+        self.store.insert_task(task);
+        self.notify();
+    }
+
+    pub fn notify(&self) {
+        let _ = self.notifier.send(());
+    }
+}

+ 32 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/store.rs

@@ -0,0 +1,32 @@
+use crate::services::tasks::task::Task;
+use crate::services::tasks::TaskId;
+use std::collections::HashMap;
+use std::sync::atomic::AtomicU32;
+use std::sync::atomic::Ordering::SeqCst;
+
+pub struct GridTaskStore {
+    tasks: HashMap<TaskId, Task>,
+    task_id_counter: AtomicU32,
+}
+
+impl GridTaskStore {
+    pub fn new() -> Self {
+        Self {
+            tasks: HashMap::new(),
+            task_id_counter: AtomicU32::new(0),
+        }
+    }
+
+    pub fn insert_task(&mut self, task: Task) {
+        self.tasks.insert(task.id, task);
+    }
+
+    pub fn remove_task(&mut self, task_id: &TaskId) -> Option<Task> {
+        self.tasks.remove(task_id)
+    }
+    #[allow(dead_code)]
+    pub fn next_task_id(&self) -> TaskId {
+        let _ = self.task_id_counter.fetch_add(1, SeqCst);
+        self.task_id_counter.load(SeqCst)
+    }
+}

+ 69 - 0
frontend/rust-lib/flowy-grid/src/services/tasks/task.rs

@@ -0,0 +1,69 @@
+use crate::services::tasks::queue::TaskHandlerId;
+use std::cmp::Ordering;
+
+#[derive(Eq, Debug, Clone, Copy)]
+pub enum TaskType {
+    /// Remove the row if it doesn't satisfy the filter.
+    Filter,
+    /// Generate snapshot for grid, unused by now.
+    Snapshot,
+}
+
+impl PartialEq for TaskType {
+    fn eq(&self, other: &Self) -> bool {
+        matches!(
+            (self, other),
+            (Self::Filter, Self::Filter) | (Self::Snapshot, Self::Snapshot)
+        )
+    }
+}
+
+pub type TaskId = u32;
+
+#[derive(Eq, Debug, Clone, Copy)]
+pub struct PendingTask {
+    pub ty: TaskType,
+    pub id: TaskId,
+}
+
+impl PartialEq for PendingTask {
+    fn eq(&self, other: &Self) -> bool {
+        self.id.eq(&other.id)
+    }
+}
+
+impl PartialOrd for PendingTask {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for PendingTask {
+    fn cmp(&self, other: &Self) -> Ordering {
+        match (self.ty, other.ty) {
+            (TaskType::Snapshot, TaskType::Snapshot) => Ordering::Equal,
+            (TaskType::Snapshot, _) => Ordering::Greater,
+            (_, TaskType::Snapshot) => Ordering::Less,
+            (TaskType::Filter, TaskType::Filter) => self.id.cmp(&other.id),
+        }
+    }
+}
+
+pub type ContentId = String;
+
+pub enum TaskContent {
+    Snapshot { content_id: ContentId },
+    Filter,
+}
+
+pub struct Task {
+    pub hid: TaskHandlerId,
+    pub id: TaskId,
+    pub content: TaskContent,
+}
+
+impl Task {
+    pub fn is_finished(&self) -> bool {
+        todo!()
+    }
+}

+ 8 - 8
frontend/rust-lib/flowy-grid/tests/grid/block_test.rs

@@ -1,13 +1,13 @@
 use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
-use flowy_grid_data_model::revision::{GridBlockRevision, GridBlockRevisionChangeset};
+use flowy_grid_data_model::revision::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset};
 
 #[tokio::test]
 async fn grid_create_block() {
-    let grid_block = GridBlockRevision::new();
+    let block_meta_rev = GridBlockMetaRevision::new();
     let scripts = vec![
         AssertBlockCount(1),
-        CreateBlock { block: grid_block },
+        CreateBlock { block: block_meta_rev },
         AssertBlockCount(2),
     ];
     GridEditorTest::new().await.run_scripts(scripts).await;
@@ -15,10 +15,10 @@ async fn grid_create_block() {
 
 #[tokio::test]
 async fn grid_update_block() {
-    let grid_block = GridBlockRevision::new();
-    let mut cloned_grid_block = grid_block.clone();
-    let changeset = GridBlockRevisionChangeset {
-        block_id: grid_block.block_id.clone(),
+    let block_meta_rev = GridBlockMetaRevision::new();
+    let mut cloned_grid_block = block_meta_rev.clone();
+    let changeset = GridBlockMetaRevisionChangeset {
+        block_id: block_meta_rev.block_id.clone(),
         start_row_index: Some(2),
         row_count: Some(10),
     };
@@ -28,7 +28,7 @@ async fn grid_update_block() {
 
     let scripts = vec![
         AssertBlockCount(1),
-        CreateBlock { block: grid_block },
+        CreateBlock { block: block_meta_rev },
         UpdateBlock { changeset },
         AssertBlockCount(2),
         AssertBlockEqual {

+ 2 - 1
frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs

@@ -1,3 +1,4 @@
+use crate::grid::field_util::make_date_cell_string;
 use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use flowy_grid::services::field::{MultiSelectTypeOption, SelectOptionCellContentChangeset, SingleSelectTypeOption};
@@ -8,7 +9,7 @@ async fn grid_cell_update() {
     let mut test = GridEditorTest::new().await;
     let field_revs = &test.field_revs;
     let row_revs = &test.row_revs;
-    let grid_blocks = &test.grid_block_revs;
+    let grid_blocks = &test.block_meta_revs;
 
     // For the moment, We only have one block to store rows
     let block_id = &grid_blocks.first().unwrap().block_id;

+ 1 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_test.rs

@@ -1,3 +1,4 @@
+use crate::grid::field_util::*;
 use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use flowy_grid::services::field::{SelectOption, SingleSelectTypeOption};

+ 81 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_util.rs

@@ -0,0 +1,81 @@
+use flowy_grid::services::field::*;
+
+use flowy_grid_data_model::entities::*;
+use flowy_grid_data_model::revision::*;
+
+pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
+    let field_rev = FieldBuilder::new(RichTextTypeOptionBuilder::default())
+        .name("Name")
+        .visibility(true)
+        .build();
+
+    let cloned_field_rev = field_rev.clone();
+
+    let type_option_data = field_rev
+        .get_type_option_entry::<RichTextTypeOption>(&field_rev.field_type)
+        .unwrap()
+        .protobuf_bytes()
+        .to_vec();
+
+    let field = Field {
+        id: field_rev.id,
+        name: field_rev.name,
+        desc: field_rev.desc,
+        field_type: field_rev.field_type,
+        frozen: field_rev.frozen,
+        visibility: field_rev.visibility,
+        width: field_rev.width,
+        is_primary: false,
+    };
+
+    let params = InsertFieldParams {
+        grid_id: grid_id.to_owned(),
+        field,
+        type_option_data,
+        start_field_id: None,
+    };
+    (params, cloned_field_rev)
+}
+
+pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
+    let single_select = SingleSelectTypeOptionBuilder::default()
+        .option(SelectOption::new("Done"))
+        .option(SelectOption::new("Progress"));
+
+    let field_rev = FieldBuilder::new(single_select).name("Name").visibility(true).build();
+    let cloned_field_rev = field_rev.clone();
+    let type_option_data = field_rev
+        .get_type_option_entry::<SingleSelectTypeOption>(&field_rev.field_type)
+        .unwrap()
+        .protobuf_bytes()
+        .to_vec();
+
+    let field = Field {
+        id: field_rev.id,
+        name: field_rev.name,
+        desc: field_rev.desc,
+        field_type: field_rev.field_type,
+        frozen: field_rev.frozen,
+        visibility: field_rev.visibility,
+        width: field_rev.width,
+        is_primary: false,
+    };
+
+    let params = InsertFieldParams {
+        grid_id: grid_id.to_owned(),
+        field,
+        type_option_data,
+        start_field_id: None,
+    };
+    (params, cloned_field_rev)
+}
+
+//  The grid will contains all existing field types and there are three empty rows in this grid.
+
+pub fn make_date_cell_string(s: &str) -> String {
+    serde_json::to_string(&DateCellContentChangeset {
+        date: Some(s.to_string()),
+        time: None,
+    })
+    .unwrap()
+}

+ 43 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs

@@ -0,0 +1,43 @@
+use crate::grid::script::EditorScript::*;
+use crate::grid::script::*;
+use flowy_grid_data_model::entities::{CreateGridFilterPayload, TextFilterCondition};
+
+#[tokio::test]
+async fn grid_filter_create_test() {
+    let test = GridEditorTest::new().await;
+    let field_rev = test.text_field();
+    let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
+    let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
+    GridEditorTest::new().await.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+#[should_panic]
+async fn grid_filter_invalid_condition_panic_test() {
+    let test = GridEditorTest::new().await;
+    let field_rev = test.text_field();
+
+    // 100 is not a valid condition, so this test should be panic.
+    let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned()));
+    let scripts = vec![InsertGridTableFilter { payload }];
+    GridEditorTest::new().await.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_delete_test() {
+    let mut test = GridEditorTest::new().await;
+    let field_rev = test.text_field();
+    let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
+    let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
+    test.run_scripts(scripts).await;
+
+    let filter = test.grid_filters().await.pop().unwrap();
+    test.run_scripts(vec![
+        DeleteGridTableFilter { filter_id: filter.id },
+        AssertTableFilterCount { count: 0 },
+    ])
+    .await;
+}
+
+#[tokio::test]
+async fn grid_filter_get_rows_test() {}

+ 3 - 1
frontend/rust-lib/flowy-grid/tests/grid/mod.rs

@@ -1,6 +1,8 @@
 mod block_test;
 mod cell_test;
 mod field_test;
+mod field_util;
+mod filter_test;
 mod row_test;
+mod row_util;
 mod script;
-mod setting_test;

+ 15 - 14
frontend/rust-lib/flowy-grid/tests/grid/row_test.rs

@@ -1,23 +1,24 @@
+use crate::grid::field_util::*;
+use crate::grid::row_util::GridRowTestBuilder;
 use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use chrono::NaiveDateTime;
 use flowy_grid::services::field::{
     DateCellData, MultiSelectTypeOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
 };
-use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowRevisionBuilder};
+use flowy_grid::services::row::{decode_cell_data, CreateRowRevisionBuilder};
 use flowy_grid_data_model::entities::FieldType;
 use flowy_grid_data_model::revision::RowMetaChangeset;
 
 #[tokio::test]
 async fn grid_create_row_count_test() {
     let test = GridEditorTest::new().await;
-    let create_row_context = CreateRowRevisionBuilder::new(&test.field_revs).build();
     let scripts = vec![
         AssertRowCount(3),
         CreateEmptyRow,
         CreateEmptyRow,
         CreateRow {
-            context: create_row_context,
+            payload: GridRowTestBuilder::new(&test).build(),
         },
         AssertRowCount(6),
     ];
@@ -27,15 +28,15 @@ async fn grid_create_row_count_test() {
 #[tokio::test]
 async fn grid_update_row() {
     let mut test = GridEditorTest::new().await;
-    let context = CreateRowRevisionBuilder::new(&test.field_revs).build();
+    let payload = GridRowTestBuilder::new(&test).build();
     let changeset = RowMetaChangeset {
-        row_id: context.row_id.clone(),
+        row_id: payload.row_id.clone(),
         height: None,
         visibility: None,
         cell_by_field_id: Default::default(),
     };
 
-    let scripts = vec![AssertRowCount(3), CreateRow { context }, UpdateRow { changeset }];
+    let scripts = vec![AssertRowCount(3), CreateRow { payload }, UpdateRow { changeset }];
     test.run_scripts(scripts).await;
 
     let expected_row = (&*test.row_revs.last().cloned().unwrap()).clone();
@@ -46,13 +47,13 @@ async fn grid_update_row() {
 #[tokio::test]
 async fn grid_delete_row() {
     let mut test = GridEditorTest::new().await;
-    let context_1 = CreateRowRevisionBuilder::new(&test.field_revs).build();
-    let context_2 = CreateRowRevisionBuilder::new(&test.field_revs).build();
-    let row_ids = vec![context_1.row_id.clone(), context_2.row_id.clone()];
+    let payload1 = GridRowTestBuilder::new(&test).build();
+    let payload2 = GridRowTestBuilder::new(&test).build();
+    let row_ids = vec![payload1.row_id.clone(), payload2.row_id.clone()];
     let scripts = vec![
         AssertRowCount(3),
-        CreateRow { context: context_1 },
-        CreateRow { context: context_2 },
+        CreateRow { payload: payload1 },
+        CreateRow { payload: payload2 },
         AssertBlockCount(1),
         AssertBlock {
             block_index: 0,
@@ -110,7 +111,7 @@ async fn grid_row_add_cells_test() {
         }
     }
     let context = builder.build();
-    let scripts = vec![CreateRow { context }, AssertGridRevisionPad];
+    let scripts = vec![CreateRow { payload: context }, AssertGridRevisionPad];
     test.run_scripts(scripts).await;
 }
 
@@ -136,12 +137,12 @@ async fn grid_row_add_date_cell_test() {
     let date_field = date_field.unwrap();
     let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
     assert_eq!(
-        decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
+        decode_cell_data(cell_data.data.clone(), &date_field)
             .parse::<DateCellData>()
             .unwrap()
             .date,
         "2022/03/16",
     );
-    let scripts = vec![CreateRow { context }];
+    let scripts = vec![CreateRow { payload: context }];
     test.run_scripts(scripts).await;
 }

+ 72 - 0
frontend/rust-lib/flowy-grid/tests/grid/row_util.rs

@@ -0,0 +1,72 @@
+use crate::grid::script::GridEditorTest;
+use flowy_grid::services::field::DateCellContentChangeset;
+use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
+use flowy_grid_data_model::entities::FieldType;
+use flowy_grid_data_model::revision::FieldRevision;
+use strum::EnumCount;
+
+pub struct GridRowTestBuilder<'a> {
+    test: &'a GridEditorTest,
+    inner_builder: CreateRowRevisionBuilder<'a>,
+}
+
+impl<'a> GridRowTestBuilder<'a> {
+    pub fn new(test: &'a GridEditorTest) -> Self {
+        assert_eq!(test.field_revs.len(), FieldType::COUNT);
+
+        let inner_builder = CreateRowRevisionBuilder::new(&test.field_revs);
+        Self { test, inner_builder }
+    }
+    #[allow(dead_code)]
+    pub fn update_text_cell(mut self, data: String) -> Self {
+        let text_field = self.field_rev_with_type(&FieldType::DateTime);
+        self.inner_builder.add_cell(&text_field.id, data).unwrap();
+        self
+    }
+
+    #[allow(dead_code)]
+    pub fn update_number_cell(mut self, data: String) -> Self {
+        let number_field = self.field_rev_with_type(&FieldType::DateTime);
+        self.inner_builder.add_cell(&number_field.id, data).unwrap();
+        self
+    }
+
+    #[allow(dead_code)]
+    pub fn update_date_cell(mut self, value: i64) -> Self {
+        let value = serde_json::to_string(&DateCellContentChangeset {
+            date: Some(value.to_string()),
+            time: None,
+        })
+        .unwrap();
+        let date_field = self.field_rev_with_type(&FieldType::DateTime);
+        self.inner_builder.add_cell(&date_field.id, value).unwrap();
+        self
+    }
+
+    #[allow(dead_code)]
+    pub fn update_checkbox_cell(mut self, data: bool) -> Self {
+        let number_field = self.field_rev_with_type(&FieldType::Checkbox);
+        self.inner_builder.add_cell(&number_field.id, data.to_string()).unwrap();
+        self
+    }
+
+    #[allow(dead_code)]
+    pub fn update_url_cell(mut self, data: String) -> Self {
+        let number_field = self.field_rev_with_type(&FieldType::Checkbox);
+        self.inner_builder.add_cell(&number_field.id, data).unwrap();
+        self
+    }
+
+    pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision {
+        self.test
+            .field_revs
+            .iter()
+            .find(|field_rev| &field_rev.field_type == field_type)
+            .unwrap()
+            .clone()
+    }
+
+    pub fn build(self) -> CreateRowRevisionPayload {
+        self.inner_builder.build()
+    }
+}

+ 66 - 94
frontend/rust-lib/flowy-grid/tests/grid/script.rs

@@ -1,7 +1,9 @@
+#![cfg_attr(rustfmt, rustfmt::skip)]
 use bytes::Bytes;
 use flowy_grid::services::field::*;
 use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor};
 use flowy_grid::services::row::CreateRowRevisionPayload;
+use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use flowy_grid_data_model::entities::*;
 use flowy_grid_data_model::revision::*;
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
@@ -30,10 +32,10 @@ pub enum EditorScript {
         field_rev: FieldRevision,
     },
     CreateBlock {
-        block: GridBlockRevision,
+        block: GridBlockMetaRevision,
     },
     UpdateBlock {
-        changeset: GridBlockRevisionChangeset,
+        changeset: GridBlockMetaRevisionChangeset,
     },
     AssertBlockCount(usize),
     AssertBlock {
@@ -43,11 +45,11 @@ pub enum EditorScript {
     },
     AssertBlockEqual {
         block_index: usize,
-        block: GridBlockRevision,
+        block: GridBlockMetaRevision,
     },
     CreateEmptyRow,
     CreateRow {
-        context: CreateRowRevisionPayload,
+        payload: CreateRowRevisionPayload,
     },
     UpdateRow {
         changeset: RowMetaChangeset,
@@ -63,11 +65,22 @@ pub enum EditorScript {
         is_err: bool,
     },
     AssertRowCount(usize),
+    #[allow(dead_code)]
     UpdateGridSetting {
         params: GridSettingChangesetParams,
     },
+    InsertGridTableFilter {
+        payload: CreateGridFilterPayload,
+    },
+    AssertTableFilterCount {
+        count: i32,
+    },
+    DeleteGridTableFilter {
+        filter_id: String,
+    },
+    #[allow(dead_code)]
     AssertGridSetting {
-        expected_setting: GridSettingRevision,
+        expected_setting: GridSetting,
     },
     AssertGridRevisionPad,
 }
@@ -77,7 +90,7 @@ pub struct GridEditorTest {
     pub grid_id: String,
     pub editor: Arc<GridRevisionEditor>,
     pub field_revs: Vec<FieldRevision>,
-    pub grid_block_revs: Vec<GridBlockRevision>,
+    pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
     pub row_revs: Vec<Arc<RowRevision>>,
     pub field_count: usize,
 
@@ -88,15 +101,15 @@ impl GridEditorTest {
     pub async fn new() -> Self {
         let sdk = FlowySDKTest::default();
         let _ = sdk.init_user().await;
-        let build_context = make_test_grid();
+        let build_context = make_all_field_test_grid();
         let view_data: Bytes = build_context.into();
         let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
         let field_revs = editor.get_field_revs::<FieldOrder>(None).await.unwrap();
-        let grid_blocks = editor.get_block_metas().await.unwrap();
+        let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
         let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs;
         assert_eq!(row_revs.len(), 3);
-        assert_eq!(grid_blocks.len(), 1);
+        assert_eq!(block_meta_revs.len(), 1);
 
         // It seems like you should add the field in the make_test_grid() function.
         // Because we assert the initialize count of the fields is equal to FieldType::COUNT.
@@ -108,7 +121,7 @@ impl GridEditorTest {
             grid_id,
             editor,
             field_revs,
-            grid_block_revs: grid_blocks,
+            block_meta_revs,
             row_revs,
             field_count: FieldType::COUNT,
             row_order_by_row_id: HashMap::default(),
@@ -162,40 +175,40 @@ impl GridEditorTest {
             }
             EditorScript::CreateBlock { block } => {
                 self.editor.create_block(block).await.unwrap();
-                self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             EditorScript::UpdateBlock { changeset: change } => {
                 self.editor.update_block(change).await.unwrap();
             }
             EditorScript::AssertBlockCount(count) => {
-                assert_eq!(self.editor.get_block_metas().await.unwrap().len(), count);
+                assert_eq!(self.editor.get_block_meta_revs().await.unwrap().len(), count);
             }
             EditorScript::AssertBlock {
                 block_index,
                 row_count,
                 start_row_index,
             } => {
-                assert_eq!(self.grid_block_revs[block_index].row_count, row_count);
-                assert_eq!(self.grid_block_revs[block_index].start_row_index, start_row_index);
+                assert_eq!(self.block_meta_revs[block_index].row_count, row_count);
+                assert_eq!(self.block_meta_revs[block_index].start_row_index, start_row_index);
             }
             EditorScript::AssertBlockEqual { block_index, block } => {
-                let blocks = self.editor.get_block_metas().await.unwrap();
+                let blocks = self.editor.get_block_meta_revs().await.unwrap();
                 let compared_block = blocks[block_index].clone();
-                assert_eq!(compared_block, block);
+                assert_eq!(compared_block, Arc::new(block));
             }
             EditorScript::CreateEmptyRow => {
                 let row_order = self.editor.create_row(None).await.unwrap();
                 self.row_order_by_row_id.insert(row_order.row_id.clone(), row_order);
                 self.row_revs = self.get_row_revs().await;
-                self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
-            EditorScript::CreateRow { context } => {
+            EditorScript::CreateRow { payload: context } => {
                 let row_orders = self.editor.insert_rows(vec![context]).await.unwrap();
                 for row_order in row_orders {
                     self.row_order_by_row_id.insert(row_order.row_id.clone(), row_order);
                 }
                 self.row_revs = self.get_row_revs().await;
-                self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             EditorScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
             EditorScript::DeleteRows { row_ids } => {
@@ -206,7 +219,7 @@ impl GridEditorTest {
 
                 self.editor.delete_rows(row_orders).await.unwrap();
                 self.row_revs = self.get_row_revs().await;
-                self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             EditorScript::AssertRow { expected_row } => {
                 let row = &*self
@@ -239,6 +252,26 @@ impl GridEditorTest {
             EditorScript::UpdateGridSetting { params } => {
                 let _ = self.editor.update_grid_setting(params).await.unwrap();
             }
+            EditorScript::InsertGridTableFilter { payload } => {
+                let params: CreateGridFilterParams = payload.try_into().unwrap();
+                let layout_type = GridLayoutType::Table;
+                let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
+                    .insert_filter(params)
+                    .build();
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
+            }
+            EditorScript::AssertTableFilterCount { count } => {
+                let layout_type = GridLayoutType::Table;
+                let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
+                assert_eq!(count as usize, filters.len());
+            }
+            EditorScript::DeleteGridTableFilter { filter_id } => {
+                let layout_type = GridLayoutType::Table;
+                let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
+                    .delete_filter(&filter_id)
+                    .build();
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
+            }
             EditorScript::AssertGridSetting { expected_setting } => {
                 let setting = self.editor.get_grid_setting().await.unwrap();
                 assert_eq!(expected_setting, setting);
@@ -261,76 +294,23 @@ impl GridEditorTest {
             .unwrap()
             .row_revs
     }
-}
-
-pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
-    let field_rev = FieldBuilder::new(RichTextTypeOptionBuilder::default())
-        .name("Name")
-        .visibility(true)
-        .build();
-
-    let cloned_field_rev = field_rev.clone();
 
-    let type_option_data = field_rev
-        .get_type_option_entry::<RichTextTypeOption>(&field_rev.field_type)
-        .unwrap()
-        .protobuf_bytes()
-        .to_vec();
-
-    let field = Field {
-        id: field_rev.id,
-        name: field_rev.name,
-        desc: field_rev.desc,
-        field_type: field_rev.field_type,
-        frozen: field_rev.frozen,
-        visibility: field_rev.visibility,
-        width: field_rev.width,
-        is_primary: false,
-    };
-
-    let params = InsertFieldParams {
-        grid_id: grid_id.to_owned(),
-        field,
-        type_option_data,
-        start_field_id: None,
-    };
-    (params, cloned_field_rev)
-}
-
-pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
-    let single_select = SingleSelectTypeOptionBuilder::default()
-        .option(SelectOption::new("Done"))
-        .option(SelectOption::new("Progress"));
-
-    let field_rev = FieldBuilder::new(single_select).name("Name").visibility(true).build();
-    let cloned_field_rev = field_rev.clone();
-    let type_option_data = field_rev
-        .get_type_option_entry::<SingleSelectTypeOption>(&field_rev.field_type)
-        .unwrap()
-        .protobuf_bytes()
-        .to_vec();
-
-    let field = Field {
-        id: field_rev.id,
-        name: field_rev.name,
-        desc: field_rev.desc,
-        field_type: field_rev.field_type,
-        frozen: field_rev.frozen,
-        visibility: field_rev.visibility,
-        width: field_rev.width,
-        is_primary: false,
-    };
+    pub async fn grid_filters(&self) -> Vec<GridFilter> {
+        let layout_type = GridLayoutType::Table;
+        self.editor.get_grid_filter(&layout_type).await.unwrap()
+    }
 
-    let params = InsertFieldParams {
-        grid_id: grid_id.to_owned(),
-        field,
-        type_option_data,
-        start_field_id: None,
-    };
-    (params, cloned_field_rev)
+    pub fn text_field(&self) -> &FieldRevision {
+        self.field_revs
+            .iter()
+            .filter(|field_rev| field_rev.field_type == FieldType::RichText)
+            .collect::<Vec<_>>()
+            .pop()
+            .unwrap()
+    }
 }
 
-fn make_test_grid() -> BuildGridContext {
+fn make_all_field_test_grid() -> BuildGridContext {
     let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
         .name("Name")
         .visibility(true)
@@ -385,11 +365,3 @@ fn make_test_grid() -> BuildGridContext {
         .add_empty_row()
         .build()
 }
-
-pub fn make_date_cell_string(s: &str) -> String {
-    serde_json::to_string(&DateCellContentChangeset {
-        date: Some(s.to_string()),
-        time: None,
-    })
-    .unwrap()
-}

+ 0 - 1
frontend/rust-lib/flowy-grid/tests/grid/setting_test.rs

@@ -1 +0,0 @@
-

+ 2 - 2
frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs

@@ -228,7 +228,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
         let grid_manager = self.0.clone();
         let view_id = view_id.to_string();
         FutureResult::new(async move {
-            let _ = grid_manager.delete_grid(view_id)?;
+            let _ = grid_manager.delete_grid(view_id).await?;
             Ok(())
         })
     }
@@ -237,7 +237,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
         let grid_manager = self.0.clone();
         let view_id = view_id.to_string();
         FutureResult::new(async move {
-            let _ = grid_manager.close_grid(view_id)?;
+            let _ = grid_manager.close_grid(view_id).await?;
             Ok(())
         })
     }

+ 9 - 0
frontend/rust-lib/flowy-text-block/src/editor.rs

@@ -199,6 +199,15 @@ fn spawn_edit_queue(
 ) -> EditorCommandSender {
     let (sender, receiver) = mpsc::channel(1000);
     let edit_queue = EditBlockQueue::new(user, rev_manager, delta, receiver);
+    // We can use tokio::task::spawn_local here by using tokio::spawn_blocking.
+    // https://github.com/tokio-rs/tokio/issues/2095
+    // tokio::task::spawn_blocking(move || {
+    //     let rt = tokio::runtime::Handle::current();
+    //     rt.block_on(async {
+    //         let local = tokio::task::LocalSet::new();
+    //         local.run_until(edit_queue.run()).await;
+    //     });
+    // });
     tokio::spawn(edit_queue.run());
     sender
 }

+ 1 - 0
frontend/scripts/flowy-tool/src/proto/template/proto_file/struct_template.rs

@@ -11,6 +11,7 @@ static RUST_TYPE_MAP: phf::Map<&'static str, &'static str> = phf_map! {
     "i32" => "int32",
     "u64" => "uint64",
     "u32" => "uint32",
+    "u8" => "uint8",
     "Vec" => "repeated",
     "f64" => "double",
     "HashMap" => "map",

+ 1 - 1
frontend/scripts/makefile/tool.toml

@@ -36,7 +36,7 @@ run_task = { name = "remove_files_with_pattern" }
 
 [tasks.rm_shared_lib_generated_protobuf_files]
 private = true
-env = { "rm_proto_path" = "./shared-lib/**/resources/proto", "rm_protobuf_path" = "./shared-lib/**/protobuf" }
+env = { "rm_proto_path" = "../shared-lib/**/resources/proto", "rm_protobuf_path" = "../shared-lib/**/protobuf" }
 run_task = { name = "remove_files_with_pattern" }
 
 

+ 2 - 5
shared-lib/flowy-folder-data-model/src/revision/view_rev.rs

@@ -30,13 +30,10 @@ pub struct ViewRevision {
     #[serde(default)]
     pub thumbnail: String,
 
-    #[serde(default = "default_plugin_type")]
+    #[serde(default = "DEFAULT_PLUGIN_TYPE")]
     pub plugin_type: i32,
 }
-
-fn default_plugin_type() -> i32 {
-    0
-}
+const DEFAULT_PLUGIN_TYPE: fn() -> i32 = || 0;
 
 impl std::convert::From<ViewRevision> for View {
     fn from(view_serde: ViewRevision) -> Self {

+ 1 - 1
shared-lib/flowy-grid-data-model/Cargo.toml

@@ -11,7 +11,7 @@ protobuf = {version = "2.18.0"}
 bytes = "1.0"
 strum = "0.21"
 strum_macros = "0.21"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0", features = ["derive", "rc"] }
 serde_json = {version = "1.0"}
 serde_repr = "0.1"
 nanoid = "0.4.0"

+ 11 - 28
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -4,8 +4,6 @@ use crate::revision::RowRevision;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error_code::ErrorCode;
 
-use std::collections::HashMap;
-
 #[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct Grid {
     #[pb(index = 1)]
@@ -15,7 +13,7 @@ pub struct Grid {
     pub field_orders: Vec<FieldOrder>,
 
     #[pb(index = 3)]
-    pub block_orders: Vec<GridBlockOrder>,
+    pub blocks: Vec<GridBlock>,
 }
 
 #[derive(Debug, Default, Clone, ProtoBuf)]
@@ -36,12 +34,15 @@ pub struct Row {
     pub id: String,
 
     #[pb(index = 2)]
-    pub cell_by_field_id: HashMap<String, Cell>,
-
-    #[pb(index = 3)]
     pub height: i32,
 }
 
+#[derive(Debug, Default, ProtoBuf)]
+pub struct OptionalRow {
+    #[pb(index = 1, one_of)]
+    pub row: Option<Row>,
+}
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct RepeatedRow {
     #[pb(index = 1)]
@@ -66,24 +67,6 @@ impl std::convert::From<Vec<GridBlock>> for RepeatedGridBlock {
     }
 }
 
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct GridBlockOrder {
-    #[pb(index = 1)]
-    pub block_id: String,
-
-    #[pb(index = 2)]
-    pub row_orders: Vec<RowOrder>,
-}
-
-impl GridBlockOrder {
-    pub fn new(block_id: &str) -> Self {
-        GridBlockOrder {
-            block_id: block_id.to_owned(),
-            row_orders: vec![],
-        }
-    }
-}
-
 #[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct IndexRowOrder {
     #[pb(index = 1)]
@@ -168,7 +151,7 @@ impl GridRowsChangeset {
     }
 }
 
-#[derive(Debug, Default, ProtoBuf)]
+#[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct GridBlock {
     #[pb(index = 1)]
     pub id: String,
@@ -305,12 +288,12 @@ pub struct QueryGridBlocksPayload {
     pub grid_id: String,
 
     #[pb(index = 2)]
-    pub block_orders: Vec<GridBlockOrder>,
+    pub block_ids: Vec<String>,
 }
 
 pub struct QueryGridBlocksParams {
     pub grid_id: String,
-    pub block_orders: Vec<GridBlockOrder>,
+    pub block_ids: Vec<String>,
 }
 
 impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
@@ -320,7 +303,7 @@ impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         Ok(QueryGridBlocksParams {
             grid_id: grid_id.0,
-            block_orders: self.block_orders,
+            block_ids: self.block_ids,
         })
     }
 }

+ 328 - 0
shared-lib/flowy-grid-data-model/src/entities/grid_filter.rs

@@ -0,0 +1,328 @@
+use crate::parser::NotEmptyStr;
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+use flowy_error_code::ErrorCode;
+
+use crate::entities::FieldType;
+use crate::revision::{FieldRevision, GridFilterRevision};
+use std::convert::TryInto;
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridFilter {
+    #[pb(index = 1)]
+    pub id: String,
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct RepeatedGridFilter {
+    #[pb(index = 1)]
+    pub items: Vec<GridFilter>,
+}
+
+impl std::convert::From<&GridFilterRevision> for GridFilter {
+    fn from(rev: &GridFilterRevision) -> Self {
+        Self { id: rev.id.clone() }
+    }
+}
+
+impl std::convert::From<&Vec<GridFilterRevision>> for RepeatedGridFilter {
+    fn from(revs: &Vec<GridFilterRevision>) -> Self {
+        RepeatedGridFilter {
+            items: revs.iter().map(|rev| rev.into()).collect(),
+        }
+    }
+}
+
+impl std::convert::From<Vec<GridFilter>> for RepeatedGridFilter {
+    fn from(items: Vec<GridFilter>) -> Self {
+        Self { items }
+    }
+}
+
+#[derive(ProtoBuf, Debug, Default, Clone)]
+pub struct CreateGridFilterPayload {
+    #[pb(index = 1)]
+    pub field_id: String,
+
+    #[pb(index = 2)]
+    pub field_type: FieldType,
+
+    #[pb(index = 3)]
+    pub condition: i32,
+
+    #[pb(index = 4, one_of)]
+    pub content: Option<String>,
+}
+
+impl CreateGridFilterPayload {
+    #[allow(dead_code)]
+    pub fn new<T: Into<i32>>(field_rev: &FieldRevision, condition: T, content: Option<String>) -> Self {
+        Self {
+            field_id: field_rev.id.clone(),
+            field_type: field_rev.field_type.clone(),
+            condition: condition.into(),
+            content,
+        }
+    }
+}
+
+pub struct CreateGridFilterParams {
+    pub field_id: String,
+    pub field_type: FieldType,
+    pub condition: u8,
+    pub content: Option<String>,
+}
+
+impl TryInto<CreateGridFilterParams> for CreateGridFilterPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateGridFilterParams, Self::Error> {
+        let field_id = NotEmptyStr::parse(self.field_id)
+            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .0;
+        let condition = self.condition as u8;
+        match self.field_type {
+            FieldType::RichText | FieldType::Checkbox | FieldType::URL => {
+                let _ = TextFilterCondition::try_from(condition)?;
+            }
+            FieldType::Number => {
+                let _ = NumberFilterCondition::try_from(condition)?;
+            }
+            FieldType::DateTime => {
+                let _ = DateFilterCondition::try_from(condition)?;
+            }
+            FieldType::SingleSelect | FieldType::MultiSelect => {
+                let _ = SelectOptionCondition::try_from(condition)?;
+            }
+        }
+
+        Ok(CreateGridFilterParams {
+            field_id,
+            field_type: self.field_type,
+            condition,
+            content: self.content,
+        })
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridTextFilter {
+    #[pb(index = 1)]
+    pub condition: TextFilterCondition,
+
+    #[pb(index = 2, one_of)]
+    pub content: Option<String>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum TextFilterCondition {
+    Is = 0,
+    IsNot = 1,
+    Contains = 2,
+    DoesNotContain = 3,
+    StartsWith = 4,
+    EndsWith = 5,
+    TextIsEmpty = 6,
+    TextIsNotEmpty = 7,
+}
+impl std::convert::From<TextFilterCondition> for i32 {
+    fn from(value: TextFilterCondition) -> Self {
+        value as i32
+    }
+}
+
+impl std::default::Default for TextFilterCondition {
+    fn default() -> Self {
+        TextFilterCondition::Is
+    }
+}
+impl std::convert::TryFrom<u8> for TextFilterCondition {
+    type Error = ErrorCode;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(TextFilterCondition::Is),
+            1 => Ok(TextFilterCondition::IsNot),
+            2 => Ok(TextFilterCondition::Contains),
+            3 => Ok(TextFilterCondition::DoesNotContain),
+            4 => Ok(TextFilterCondition::StartsWith),
+            5 => Ok(TextFilterCondition::EndsWith),
+            6 => Ok(TextFilterCondition::TextIsEmpty),
+            7 => Ok(TextFilterCondition::TextIsNotEmpty),
+            _ => Err(ErrorCode::InvalidData),
+        }
+    }
+}
+
+impl std::convert::From<GridFilterRevision> for GridTextFilter {
+    fn from(rev: GridFilterRevision) -> Self {
+        GridTextFilter {
+            condition: TextFilterCondition::try_from(rev.condition).unwrap_or(TextFilterCondition::Is),
+            content: rev.content,
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridNumberFilter {
+    #[pb(index = 1)]
+    pub condition: NumberFilterCondition,
+
+    #[pb(index = 2, one_of)]
+    pub content: Option<String>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum NumberFilterCondition {
+    Equal = 0,
+    NotEqual = 1,
+    GreaterThan = 2,
+    LessThan = 3,
+    GreaterThanOrEqualTo = 4,
+    LessThanOrEqualTo = 5,
+    NumberIsEmpty = 6,
+    NumberIsNotEmpty = 7,
+}
+impl std::default::Default for NumberFilterCondition {
+    fn default() -> Self {
+        NumberFilterCondition::Equal
+    }
+}
+
+impl std::convert::From<NumberFilterCondition> for i32 {
+    fn from(value: NumberFilterCondition) -> Self {
+        value as i32
+    }
+}
+impl std::convert::TryFrom<u8> for NumberFilterCondition {
+    type Error = ErrorCode;
+
+    fn try_from(n: u8) -> Result<Self, Self::Error> {
+        match n {
+            0 => Ok(NumberFilterCondition::Equal),
+            1 => Ok(NumberFilterCondition::NotEqual),
+            2 => Ok(NumberFilterCondition::GreaterThan),
+            3 => Ok(NumberFilterCondition::LessThan),
+            4 => Ok(NumberFilterCondition::GreaterThanOrEqualTo),
+            5 => Ok(NumberFilterCondition::LessThanOrEqualTo),
+            6 => Ok(NumberFilterCondition::NumberIsEmpty),
+            7 => Ok(NumberFilterCondition::NumberIsNotEmpty),
+            _ => Err(ErrorCode::InvalidData),
+        }
+    }
+}
+
+impl std::convert::From<GridFilterRevision> for GridNumberFilter {
+    fn from(rev: GridFilterRevision) -> Self {
+        GridNumberFilter {
+            condition: NumberFilterCondition::try_from(rev.condition).unwrap_or(NumberFilterCondition::Equal),
+            content: rev.content,
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridSelectOptionFilter {
+    #[pb(index = 1)]
+    pub condition: SelectOptionCondition,
+
+    #[pb(index = 2, one_of)]
+    pub content: Option<String>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum SelectOptionCondition {
+    OptionIs = 0,
+    OptionIsNot = 1,
+    OptionIsEmpty = 2,
+    OptionIsNotEmpty = 3,
+}
+
+impl std::convert::From<SelectOptionCondition> for i32 {
+    fn from(value: SelectOptionCondition) -> Self {
+        value as i32
+    }
+}
+
+impl std::default::Default for SelectOptionCondition {
+    fn default() -> Self {
+        SelectOptionCondition::OptionIs
+    }
+}
+
+impl std::convert::TryFrom<u8> for SelectOptionCondition {
+    type Error = ErrorCode;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(SelectOptionCondition::OptionIs),
+            1 => Ok(SelectOptionCondition::OptionIsNot),
+            2 => Ok(SelectOptionCondition::OptionIsEmpty),
+            3 => Ok(SelectOptionCondition::OptionIsNotEmpty),
+            _ => Err(ErrorCode::InvalidData),
+        }
+    }
+}
+
+impl std::convert::From<GridFilterRevision> for GridSelectOptionFilter {
+    fn from(rev: GridFilterRevision) -> Self {
+        GridSelectOptionFilter {
+            condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs),
+            content: rev.content,
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridDateFilter {
+    #[pb(index = 1)]
+    pub condition: DateFilterCondition,
+
+    #[pb(index = 2, one_of)]
+    pub content: Option<String>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum DateFilterCondition {
+    DateIs = 0,
+    DateBefore = 1,
+    DateAfter = 2,
+    DateOnOrBefore = 3,
+    DateOnOrAfter = 4,
+    DateWithIn = 5,
+    DateIsEmpty = 6,
+}
+
+impl std::default::Default for DateFilterCondition {
+    fn default() -> Self {
+        DateFilterCondition::DateIs
+    }
+}
+
+impl std::convert::TryFrom<u8> for DateFilterCondition {
+    type Error = ErrorCode;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(DateFilterCondition::DateIs),
+            1 => Ok(DateFilterCondition::DateBefore),
+            2 => Ok(DateFilterCondition::DateAfter),
+            3 => Ok(DateFilterCondition::DateOnOrBefore),
+            4 => Ok(DateFilterCondition::DateOnOrAfter),
+            5 => Ok(DateFilterCondition::DateWithIn),
+            6 => Ok(DateFilterCondition::DateIsEmpty),
+            _ => Err(ErrorCode::InvalidData),
+        }
+    }
+}
+impl std::convert::From<GridFilterRevision> for GridDateFilter {
+    fn from(rev: GridFilterRevision) -> Self {
+        GridDateFilter {
+            condition: DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs),
+            content: rev.content,
+        }
+    }
+}

+ 80 - 0
shared-lib/flowy-grid-data-model/src/entities/grid_group.rs

@@ -0,0 +1,80 @@
+use crate::parser::NotEmptyStr;
+use flowy_derive::ProtoBuf;
+use flowy_error_code::ErrorCode;
+
+use crate::revision::GridGroupRevision;
+use std::convert::TryInto;
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridGroup {
+    #[pb(index = 1)]
+    pub id: String,
+
+    #[pb(index = 2, one_of)]
+    pub group_field_id: Option<String>,
+
+    #[pb(index = 3, one_of)]
+    pub sub_group_field_id: Option<String>,
+}
+
+impl std::convert::From<&GridGroupRevision> for GridGroup {
+    fn from(rev: &GridGroupRevision) -> Self {
+        GridGroup {
+            id: rev.id.clone(),
+            group_field_id: rev.field_id.clone(),
+            sub_group_field_id: rev.sub_field_id.clone(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct RepeatedGridGroup {
+    #[pb(index = 1)]
+    pub items: Vec<GridGroup>,
+}
+
+impl std::convert::From<Vec<GridGroup>> for RepeatedGridGroup {
+    fn from(items: Vec<GridGroup>) -> Self {
+        Self { items }
+    }
+}
+
+impl std::convert::From<&Vec<GridGroupRevision>> for RepeatedGridGroup {
+    fn from(revs: &Vec<GridGroupRevision>) -> Self {
+        RepeatedGridGroup {
+            items: revs.iter().map(|rev| rev.into()).collect(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct CreateGridGroupPayload {
+    #[pb(index = 1, one_of)]
+    pub field_id: Option<String>,
+
+    #[pb(index = 2, one_of)]
+    pub sub_field_id: Option<String>,
+}
+
+pub struct CreateGridGroupParams {
+    pub field_id: Option<String>,
+    pub sub_field_id: Option<String>,
+}
+
+impl TryInto<CreateGridGroupParams> for CreateGridGroupPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateGridGroupParams, Self::Error> {
+        let field_id = match self.field_id {
+            None => None,
+            Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
+        };
+
+        let sub_field_id = match self.sub_field_id {
+            None => None,
+            Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
+        };
+
+        Ok(CreateGridGroupParams { field_id, sub_field_id })
+    }
+}

+ 102 - 36
shared-lib/flowy-grid-data-model/src/entities/grid_setting.rs

@@ -1,4 +1,9 @@
-use crate::parser::{NotEmptyStr, ViewFilterParser, ViewGroupParser, ViewSortParser};
+use crate::entities::{
+    CreateGridFilterParams, CreateGridFilterPayload, CreateGridGroupParams, CreateGridGroupPayload,
+    CreateGridSortParams, CreateGridSortPayload, RepeatedGridFilter, RepeatedGridGroup, RepeatedGridSort,
+};
+use crate::parser::NotEmptyStr;
+use crate::revision::{GridLayoutRevision, GridSettingRevision};
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error_code::ErrorCode;
 use std::collections::HashMap;
@@ -7,13 +12,41 @@ use std::convert::TryInto;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GridSetting {
     #[pb(index = 1)]
-    pub filter: HashMap<String, GridFilter>,
+    pub filters_by_layout_ty: HashMap<String, RepeatedGridFilter>,
 
     #[pb(index = 2)]
-    pub group: HashMap<String, GridGroup>,
+    pub groups_by_layout_ty: HashMap<String, RepeatedGridGroup>,
 
     #[pb(index = 3)]
-    pub sort: HashMap<String, GridSort>,
+    pub sorts_by_layout_ty: HashMap<String, RepeatedGridSort>,
+}
+
+impl std::convert::From<&GridSettingRevision> for GridSetting {
+    fn from(rev: &GridSettingRevision) -> Self {
+        let filters_by_layout_ty: HashMap<String, RepeatedGridFilter> = rev
+            .filters
+            .iter()
+            .map(|(layout_rev, filter_revs)| (layout_rev.to_string(), filter_revs.into()))
+            .collect();
+
+        let groups_by_layout_ty: HashMap<String, RepeatedGridGroup> = rev
+            .groups
+            .iter()
+            .map(|(layout_rev, group_revs)| (layout_rev.to_string(), group_revs.into()))
+            .collect();
+
+        let sorts_by_layout_ty: HashMap<String, RepeatedGridSort> = rev
+            .sorts
+            .iter()
+            .map(|(layout_rev, sort_revs)| (layout_rev.to_string(), sort_revs.into()))
+            .collect();
+
+        GridSetting {
+            filters_by_layout_ty,
+            groups_by_layout_ty,
+            sorts_by_layout_ty,
+        }
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
@@ -29,25 +62,22 @@ impl std::default::Default for GridLayoutType {
     }
 }
 
-#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct GridFilter {
-    #[pb(index = 1, one_of)]
-    pub field_id: Option<String>,
-}
-
-#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct GridGroup {
-    #[pb(index = 1, one_of)]
-    pub group_field_id: Option<String>,
-
-    #[pb(index = 2, one_of)]
-    pub sub_group_field_id: Option<String>,
+impl std::convert::From<GridLayoutRevision> for GridLayoutType {
+    fn from(rev: GridLayoutRevision) -> Self {
+        match rev {
+            GridLayoutRevision::Table => GridLayoutType::Table,
+            GridLayoutRevision::Board => GridLayoutType::Board,
+        }
+    }
 }
 
-#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct GridSort {
-    #[pb(index = 1, one_of)]
-    pub field_id: Option<String>,
+impl std::convert::From<GridLayoutType> for GridLayoutRevision {
+    fn from(layout: GridLayoutType) -> Self {
+        match layout {
+            GridLayoutType::Table => GridLayoutRevision::Table,
+            GridLayoutType::Board => GridLayoutRevision::Board,
+        }
+    }
 }
 
 #[derive(Default, ProtoBuf)]
@@ -59,21 +89,39 @@ pub struct GridSettingChangesetPayload {
     pub layout_type: GridLayoutType,
 
     #[pb(index = 3, one_of)]
-    pub filter: Option<GridFilter>,
+    pub insert_filter: Option<CreateGridFilterPayload>,
 
     #[pb(index = 4, one_of)]
-    pub group: Option<GridGroup>,
+    pub delete_filter: Option<String>,
 
     #[pb(index = 5, one_of)]
-    pub sort: Option<GridSort>,
+    pub insert_group: Option<CreateGridGroupPayload>,
+
+    #[pb(index = 6, one_of)]
+    pub delete_group: Option<String>,
+
+    #[pb(index = 7, one_of)]
+    pub insert_sort: Option<CreateGridSortPayload>,
+
+    #[pb(index = 8, one_of)]
+    pub delete_sort: Option<String>,
 }
 
 pub struct GridSettingChangesetParams {
     pub grid_id: String,
     pub layout_type: GridLayoutType,
-    pub filter: Option<GridFilter>,
-    pub group: Option<GridGroup>,
-    pub sort: Option<GridSort>,
+    pub insert_filter: Option<CreateGridFilterParams>,
+    pub delete_filter: Option<String>,
+    pub insert_group: Option<CreateGridGroupParams>,
+    pub delete_group: Option<String>,
+    pub insert_sort: Option<CreateGridSortParams>,
+    pub delete_sort: Option<String>,
+}
+
+impl GridSettingChangesetParams {
+    pub fn is_filter_changed(&self) -> bool {
+        self.insert_filter.is_some() || self.delete_filter.is_some()
+    }
 }
 
 impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayload {
@@ -84,27 +132,45 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayload {
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
 
-        let filter = match self.filter {
+        let insert_filter = match self.insert_filter {
+            None => None,
+            Some(payload) => Some(payload.try_into()?),
+        };
+
+        let delete_filter = match self.delete_filter {
+            None => None,
+            Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
+        };
+
+        let insert_group = match self.insert_group {
+            Some(payload) => Some(payload.try_into()?),
+            None => None,
+        };
+
+        let delete_group = match self.delete_group {
             None => None,
-            Some(filter) => Some(ViewFilterParser::parse(filter)?),
+            Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
         };
 
-        let group = match self.group {
+        let insert_sort = match self.insert_sort {
             None => None,
-            Some(group) => Some(ViewGroupParser::parse(group)?),
+            Some(payload) => Some(payload.try_into()?),
         };
 
-        let sort = match self.sort {
+        let delete_sort = match self.delete_sort {
             None => None,
-            Some(sort) => Some(ViewSortParser::parse(sort)?),
+            Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
         };
 
         Ok(GridSettingChangesetParams {
             grid_id: view_id,
             layout_type: self.layout_type,
-            filter,
-            group,
-            sort,
+            insert_filter,
+            delete_filter,
+            insert_group,
+            delete_group,
+            insert_sort,
+            delete_sort,
         })
     }
 }

+ 68 - 0
shared-lib/flowy-grid-data-model/src/entities/grid_sort.rs

@@ -0,0 +1,68 @@
+use crate::parser::NotEmptyStr;
+use flowy_derive::ProtoBuf;
+use flowy_error_code::ErrorCode;
+
+use crate::revision::GridSortRevision;
+use std::convert::TryInto;
+
+#[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<String>,
+}
+
+impl std::convert::From<&GridSortRevision> for GridSort {
+    fn from(rev: &GridSortRevision) -> Self {
+        GridSort {
+            id: rev.id.clone(),
+
+            field_id: rev.field_id.clone(),
+        }
+    }
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct RepeatedGridSort {
+    #[pb(index = 1)]
+    pub items: Vec<GridSort>,
+}
+
+impl std::convert::From<&Vec<GridSortRevision>> for RepeatedGridSort {
+    fn from(revs: &Vec<GridSortRevision>) -> Self {
+        RepeatedGridSort {
+            items: revs.iter().map(|rev| rev.into()).collect(),
+        }
+    }
+}
+
+impl std::convert::From<Vec<GridSort>> for RepeatedGridSort {
+    fn from(items: Vec<GridSort>) -> Self {
+        Self { items }
+    }
+}
+
+#[derive(ProtoBuf, Debug, Default, Clone)]
+pub struct CreateGridSortPayload {
+    #[pb(index = 1, one_of)]
+    pub field_id: Option<String>,
+}
+
+pub struct CreateGridSortParams {
+    pub field_id: Option<String>,
+}
+
+impl TryInto<CreateGridSortParams> for CreateGridSortPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateGridSortParams, Self::Error> {
+        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 })
+    }
+}

+ 6 - 0
shared-lib/flowy-grid-data-model/src/entities/mod.rs

@@ -1,7 +1,13 @@
 mod field;
 mod grid;
+mod grid_filter;
+mod grid_group;
 mod grid_setting;
+mod grid_sort;
 
 pub use field::*;
 pub use grid::*;
+pub use grid_filter::*;
+pub use grid_group::*;
 pub use grid_setting::*;
+pub use grid_sort::*;

+ 0 - 58
shared-lib/flowy-grid-data-model/src/parser/grid_info_parser.rs

@@ -1,58 +0,0 @@
-use crate::entities::{GridFilter, GridGroup, GridSort};
-use crate::parser::NotEmptyStr;
-use flowy_error_code::ErrorCode;
-
-pub struct ViewFilterParser(pub GridFilter);
-
-impl ViewFilterParser {
-    pub fn parse(value: GridFilter) -> Result<GridFilter, ErrorCode> {
-        let field_id = match value.field_id {
-            None => None,
-            Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
-        };
-
-        Ok(GridFilter { field_id })
-    }
-}
-
-pub struct ViewGroupParser(pub GridGroup);
-
-impl ViewGroupParser {
-    pub fn parse(value: GridGroup) -> Result<GridGroup, ErrorCode> {
-        let group_field_id = match value.group_field_id {
-            None => None,
-            Some(group_field_id) => Some(
-                NotEmptyStr::parse(group_field_id)
-                    .map_err(|_| ErrorCode::FieldIdIsEmpty)?
-                    .0,
-            ),
-        };
-
-        let sub_group_field_id = match value.sub_group_field_id {
-            None => None,
-            Some(sub_group_field_id) => Some(
-                NotEmptyStr::parse(sub_group_field_id)
-                    .map_err(|_| ErrorCode::FieldIdIsEmpty)?
-                    .0,
-            ),
-        };
-
-        Ok(GridGroup {
-            group_field_id,
-            sub_group_field_id,
-        })
-    }
-}
-
-pub struct ViewSortParser(pub GridSort);
-
-impl ViewSortParser {
-    pub fn parse(value: GridSort) -> Result<GridSort, ErrorCode> {
-        let field_id = match value.field_id {
-            None => None,
-            Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
-        };
-
-        Ok(GridSort { field_id })
-    }
-}

+ 0 - 3
shared-lib/flowy-grid-data-model/src/parser/mod.rs

@@ -1,5 +1,2 @@
-mod grid_info_parser;
 mod str_parser;
-
-pub use grid_info_parser::*;
 pub use str_parser::*;

+ 94 - 0
shared-lib/flowy-grid-data-model/src/revision/grid_filter_rev.rs

@@ -0,0 +1,94 @@
+use crate::entities::NumberFilterCondition;
+use indexmap::IndexMap;
+use nanoid::nanoid;
+use serde::{Deserialize, Serialize};
+use serde_repr::*;
+use std::str::FromStr;
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
+pub struct GridFilterRevision {
+    pub id: String,
+    pub field_id: String,
+    pub condition: u8,
+    pub content: Option<String>,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
+#[repr(u8)]
+pub enum TextFilterConditionRevision {
+    Is = 0,
+    IsNot = 1,
+    Contains = 2,
+    DoesNotContain = 3,
+    StartsWith = 4,
+    EndsWith = 5,
+    IsEmpty = 6,
+    IsNotEmpty = 7,
+}
+
+impl ToString for TextFilterConditionRevision {
+    fn to_string(&self) -> String {
+        (self.clone() as u8).to_string()
+    }
+}
+
+impl FromStr for TextFilterConditionRevision {
+    type Err = serde_json::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let rev = serde_json::from_str(s)?;
+        Ok(rev)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
+#[repr(u8)]
+pub enum NumberFilterConditionRevision {
+    Equal = 0,
+    NotEqual = 1,
+    GreaterThan = 2,
+    LessThan = 3,
+    GreaterThanOrEqualTo = 4,
+    LessThanOrEqualTo = 5,
+    IsEmpty = 6,
+    IsNotEmpty = 7,
+}
+
+impl ToString for NumberFilterConditionRevision {
+    fn to_string(&self) -> String {
+        (self.clone() as u8).to_string()
+    }
+}
+
+impl FromStr for NumberFilterConditionRevision {
+    type Err = serde_json::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let rev = serde_json::from_str(s)?;
+        Ok(rev)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
+#[repr(u8)]
+pub enum SelectOptionConditionRevision {
+    OptionIs = 0,
+    OptionIsNot = 1,
+    OptionIsEmpty = 2,
+    OptionIsNotEmpty = 3,
+}
+
+impl ToString for SelectOptionConditionRevision {
+    fn to_string(&self) -> String {
+        (self.clone() as u8).to_string()
+    }
+}
+
+impl FromStr for SelectOptionConditionRevision {
+    type Err = serde_json::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let rev = serde_json::from_str(s)?;
+        Ok(rev)
+    }
+}

+ 23 - 16
shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs

@@ -30,9 +30,9 @@ pub fn gen_field_id() -> String {
 pub struct GridRevision {
     pub grid_id: String,
     pub fields: Vec<FieldRevision>,
-    pub blocks: Vec<GridBlockRevision>,
+    pub blocks: Vec<Arc<GridBlockMetaRevision>>,
 
-    #[serde(skip)]
+    #[serde(default, skip)]
     pub setting: GridSettingRevision,
 }
 
@@ -45,16 +45,25 @@ impl GridRevision {
             setting: GridSettingRevision::default(),
         }
     }
+
+    pub fn from_build_context(grid_id: &str, context: BuildGridContext) -> Self {
+        Self {
+            grid_id: grid_id.to_owned(),
+            fields: context.field_revs,
+            blocks: context.blocks.into_iter().map(Arc::new).collect(),
+            setting: Default::default(),
+        }
+    }
 }
 
 #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
-pub struct GridBlockRevision {
+pub struct GridBlockMetaRevision {
     pub block_id: String,
     pub start_row_index: i32,
     pub row_count: i32,
 }
 
-impl GridBlockRevision {
+impl GridBlockMetaRevision {
     pub fn len(&self) -> i32 {
         self.row_count
     }
@@ -64,22 +73,22 @@ impl GridBlockRevision {
     }
 }
 
-impl GridBlockRevision {
+impl GridBlockMetaRevision {
     pub fn new() -> Self {
-        GridBlockRevision {
+        GridBlockMetaRevision {
             block_id: gen_block_id(),
             ..Default::default()
         }
     }
 }
 
-pub struct GridBlockRevisionChangeset {
+pub struct GridBlockMetaRevisionChangeset {
     pub block_id: String,
     pub start_row_index: Option<i32>,
     pub row_count: Option<i32>,
 }
 
-impl GridBlockRevisionChangeset {
+impl GridBlockMetaRevisionChangeset {
     pub fn from_row_count(block_id: &str, row_count: i32) -> Self {
         Self {
             block_id: block_id.to_string(),
@@ -90,9 +99,9 @@ impl GridBlockRevisionChangeset {
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
-pub struct GridBlockRevisionData {
+pub struct GridBlockRevision {
     pub block_id: String,
-    pub rows: Vec<RowRevision>,
+    pub rows: Vec<Arc<RowRevision>>,
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
@@ -118,13 +127,11 @@ pub struct FieldRevision {
     #[serde(with = "indexmap::serde_seq")]
     pub type_options: IndexMap<String, String>,
 
-    #[serde(default = "default_is_primary")]
+    #[serde(default = "DEFAULT_IS_PRIMARY")]
     pub is_primary: bool,
 }
 
-fn default_is_primary() -> bool {
-    false
-}
+const DEFAULT_IS_PRIMARY: fn() -> bool = || false;
 
 impl FieldRevision {
     pub fn new(name: &str, desc: &str, field_type: FieldType, is_primary: bool) -> Self {
@@ -283,8 +290,8 @@ impl CellRevision {
 #[derive(Clone, Default, Deserialize, Serialize)]
 pub struct BuildGridContext {
     pub field_revs: Vec<FieldRevision>,
-    pub blocks: Vec<GridBlockRevision>,
-    pub blocks_meta_data: Vec<GridBlockRevisionData>,
+    pub blocks: Vec<GridBlockMetaRevision>,
+    pub blocks_meta_data: Vec<GridBlockRevision>,
 }
 
 impl BuildGridContext {

+ 28 - 73
shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs

@@ -1,19 +1,32 @@
-use crate::entities::{GridFilter, GridGroup, GridLayoutType, GridSetting, GridSort};
 use indexmap::IndexMap;
+use nanoid::nanoid;
 use serde::{Deserialize, Serialize};
 use serde_repr::*;
-use std::collections::HashMap;
+
+pub fn gen_grid_filter_id() -> String {
+    nanoid!(6)
+}
+
+pub fn gen_grid_group_id() -> String {
+    nanoid!(6)
+}
+
+pub fn gen_grid_sort_id() -> String {
+    nanoid!(6)
+}
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
 pub struct GridSettingRevision {
-    #[serde(with = "indexmap::serde_seq")]
-    pub filter: IndexMap<GridLayoutRevision, GridFilterRevision>,
+    pub layout: GridLayoutRevision,
 
     #[serde(with = "indexmap::serde_seq")]
-    pub group: IndexMap<GridLayoutRevision, GridGroupRevision>,
+    pub filters: IndexMap<GridLayoutRevision, Vec<GridFilterRevision>>,
 
-    #[serde(with = "indexmap::serde_seq")]
-    pub sort: IndexMap<GridLayoutRevision, GridSortRevision>,
+    #[serde(skip, with = "indexmap::serde_seq")]
+    pub groups: IndexMap<GridLayoutRevision, Vec<GridGroupRevision>>,
+
+    #[serde(skip, with = "indexmap::serde_seq")]
+    pub sorts: IndexMap<GridLayoutRevision, Vec<GridSortRevision>>,
 }
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
@@ -36,81 +49,23 @@ impl std::default::Default for GridLayoutRevision {
     }
 }
 
-impl std::convert::From<GridLayoutRevision> for GridLayoutType {
-    fn from(rev: GridLayoutRevision) -> Self {
-        match rev {
-            GridLayoutRevision::Table => GridLayoutType::Table,
-            GridLayoutRevision::Board => GridLayoutType::Board,
-        }
-    }
-}
-
-impl std::convert::From<GridLayoutType> for GridLayoutRevision {
-    fn from(layout: GridLayoutType) -> Self {
-        match layout {
-            GridLayoutType::Table => GridLayoutRevision::Table,
-            GridLayoutType::Board => GridLayoutRevision::Board,
-        }
-    }
-}
-
 #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
 pub struct GridFilterRevision {
-    pub field_id: Option<String>,
+    pub id: String,
+    pub field_id: String,
+    pub condition: u8,
+    pub content: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
 pub struct GridGroupRevision {
-    pub group_field_id: Option<String>,
-    pub sub_group_field_id: Option<String>,
+    pub id: String,
+    pub field_id: Option<String>,
+    pub sub_field_id: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
 pub struct GridSortRevision {
+    pub id: String,
     pub field_id: Option<String>,
 }
-
-impl std::convert::From<GridFilterRevision> for GridFilter {
-    fn from(rev: GridFilterRevision) -> Self {
-        GridFilter { field_id: rev.field_id }
-    }
-}
-
-impl std::convert::From<GridGroupRevision> for GridGroup {
-    fn from(rev: GridGroupRevision) -> Self {
-        GridGroup {
-            group_field_id: rev.group_field_id,
-            sub_group_field_id: rev.sub_group_field_id,
-        }
-    }
-}
-
-impl std::convert::From<GridSortRevision> for GridSort {
-    fn from(rev: GridSortRevision) -> Self {
-        GridSort { field_id: rev.field_id }
-    }
-}
-
-impl std::convert::From<GridSettingRevision> for GridSetting {
-    fn from(rev: GridSettingRevision) -> Self {
-        let filter: HashMap<String, GridFilter> = rev
-            .filter
-            .into_iter()
-            .map(|(layout_rev, filter_rev)| (layout_rev.to_string(), filter_rev.into()))
-            .collect();
-
-        let group: HashMap<String, GridGroup> = rev
-            .group
-            .into_iter()
-            .map(|(layout_rev, group_rev)| (layout_rev.to_string(), group_rev.into()))
-            .collect();
-
-        let sort: HashMap<String, GridSort> = rev
-            .sort
-            .into_iter()
-            .map(|(layout_rev, sort_rev)| (layout_rev.to_string(), sort_rev.into()))
-            .collect();
-
-        GridSetting { filter, group, sort }
-    }
-}

+ 31 - 35
shared-lib/flowy-sync/src/client_grid/grid_block_revsion_pad.rs

@@ -2,40 +2,44 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
 use flowy_grid_data_model::revision::{
-    gen_block_id, gen_row_id, CellRevision, GridBlockRevisionData, RowMetaChangeset, RowRevision,
+    gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
 };
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
-use serde::{Deserialize, Serialize};
 use std::borrow::Cow;
-
 use std::collections::HashMap;
 use std::sync::Arc;
 
 pub type GridBlockRevisionDelta = PlainTextDelta;
 pub type GridBlockRevisionDeltaBuilder = PlainTextDeltaBuilder;
 
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Clone)]
 pub struct GridBlockRevisionPad {
-    block_id: String,
-    rows: Vec<Arc<RowRevision>>,
-
-    #[serde(skip)]
+    block_revision: GridBlockRevision,
     pub(crate) delta: GridBlockRevisionDelta,
 }
 
+impl std::ops::Deref for GridBlockRevisionPad {
+    type Target = GridBlockRevision;
+
+    fn deref(&self) -> &Self::Target {
+        &self.block_revision
+    }
+}
+
 impl GridBlockRevisionPad {
-    pub async fn duplicate_data(&self, duplicated_block_id: &str) -> GridBlockRevisionData {
+    pub async fn duplicate_data(&self, duplicated_block_id: &str) -> GridBlockRevision {
         let duplicated_rows = self
+            .block_revision
             .rows
             .iter()
             .map(|row| {
                 let mut duplicated_row = row.as_ref().clone();
                 duplicated_row.id = gen_row_id();
                 duplicated_row.block_id = duplicated_block_id.to_string();
-                duplicated_row
+                Arc::new(duplicated_row)
             })
-            .collect::<Vec<RowRevision>>();
-        GridBlockRevisionData {
+            .collect::<Vec<Arc<RowRevision>>>();
+        GridBlockRevision {
             block_id: duplicated_block_id.to_string(),
             rows: duplicated_rows,
         }
@@ -43,18 +47,12 @@ impl GridBlockRevisionPad {
 
     pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> {
         let s = delta.to_str()?;
-        let meta_data: GridBlockRevisionData = serde_json::from_str(&s).map_err(|e| {
+        let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
             let msg = format!("Deserialize delta to block meta failed: {}", e);
             tracing::error!("{}", s);
             CollaborateError::internal().context(msg)
         })?;
-        let block_id = meta_data.block_id;
-        let rows = meta_data
-            .rows
-            .into_iter()
-            .map(Arc::new)
-            .collect::<Vec<Arc<RowRevision>>>();
-        Ok(Self { block_id, rows, delta })
+        Ok(Self { block_revision, delta })
     }
 
     pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
@@ -95,9 +93,10 @@ impl GridBlockRevisionPad {
         T: AsRef<str> + ToOwned + ?Sized,
     {
         match row_ids {
-            None => Ok(self.rows.to_vec()),
+            None => Ok(self.block_revision.rows.clone()),
             Some(row_ids) => {
                 let row_map = self
+                    .block_revision
                     .rows
                     .iter()
                     .map(|row| (row.id.as_str(), row.clone()))
@@ -137,11 +136,12 @@ impl GridBlockRevisionPad {
     }
 
     pub fn number_of_rows(&self) -> i32 {
-        self.rows.len() as i32
+        self.block_revision.rows.len() as i32
     }
 
     pub fn index_of_row(&self, row_id: &str) -> Option<i32> {
-        self.rows
+        self.block_revision
+            .rows
             .iter()
             .position(|row| row.id == row_id)
             .map(|index| index as i32)
@@ -190,7 +190,7 @@ impl GridBlockRevisionPad {
         F: for<'a> FnOnce(&'a mut Vec<Arc<RowRevision>>) -> CollaborateResult<Option<()>>,
     {
         let cloned_self = self.clone();
-        match f(&mut self.rows)? {
+        match f(&mut self.block_revision.rows)? {
             None => Ok(None),
             Some(_) => {
                 let old = cloned_self.to_json()?;
@@ -226,7 +226,7 @@ impl GridBlockRevisionPad {
     }
 
     pub fn to_json(&self) -> CollaborateResult<String> {
-        serde_json::to_string(self)
+        serde_json::to_string(&self.block_revision)
             .map_err(|e| CollaborateError::internal().context(format!("serial trash to json failed: {}", e)))
     }
 
@@ -245,12 +245,12 @@ pub struct GridBlockMetaChange {
     pub md5: String,
 }
 
-pub fn make_block_meta_delta(grid_block_meta_data: &GridBlockRevisionData) -> GridBlockRevisionDelta {
-    let json = serde_json::to_string(&grid_block_meta_data).unwrap();
+pub fn make_block_meta_delta(block_rev: &GridBlockRevision) -> GridBlockRevisionDelta {
+    let json = serde_json::to_string(&block_rev).unwrap();
     PlainTextDeltaBuilder::new().insert(&json).build()
 }
 
-pub fn make_block_meta_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevisionData) -> RepeatedRevision {
+pub fn make_block_meta_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
     let delta = make_block_meta_delta(grid_block_meta_data);
     let bytes = delta.to_delta_bytes();
     let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
@@ -259,17 +259,13 @@ pub fn make_block_meta_revisions(user_id: &str, grid_block_meta_data: &GridBlock
 
 impl std::default::Default for GridBlockRevisionPad {
     fn default() -> Self {
-        let block_meta_data = GridBlockRevisionData {
+        let block_revision = GridBlockRevision {
             block_id: gen_block_id(),
             rows: vec![],
         };
 
-        let delta = make_block_meta_delta(&block_meta_data);
-        GridBlockRevisionPad {
-            block_id: block_meta_data.block_id,
-            rows: block_meta_data.rows.into_iter().map(Arc::new).collect::<Vec<_>>(),
-            delta,
-        }
+        let delta = make_block_meta_delta(&block_revision);
+        GridBlockRevisionPad { block_revision, delta }
     }
 }
 

+ 12 - 11
shared-lib/flowy-sync/src/client_grid/grid_builder.rs

@@ -1,7 +1,8 @@
 use crate::errors::{CollaborateError, CollaborateResult};
 use flowy_grid_data_model::revision::{
-    BuildGridContext, FieldRevision, GridBlockRevision, GridBlockRevisionData, RowRevision,
+    BuildGridContext, FieldRevision, GridBlockMetaRevision, GridBlockRevision, RowRevision,
 };
+use std::sync::Arc;
 
 pub struct GridBuilder {
     build_context: BuildGridContext,
@@ -11,8 +12,8 @@ impl std::default::Default for GridBuilder {
     fn default() -> Self {
         let mut build_context = BuildGridContext::new();
 
-        let block_meta = GridBlockRevision::new();
-        let block_meta_data = GridBlockRevisionData {
+        let block_meta = GridBlockMetaRevision::new();
+        let block_meta_data = GridBlockRevision {
             block_id: block_meta.block_id.clone(),
             rows: vec![],
         };
@@ -32,10 +33,10 @@ impl GridBuilder {
 
     pub fn add_empty_row(mut self) -> Self {
         let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id);
-        let block_meta = self.build_context.blocks.first_mut().unwrap();
-        let block_meta_data = self.build_context.blocks_meta_data.first_mut().unwrap();
-        block_meta_data.rows.push(row);
-        block_meta.row_count += 1;
+        let block_meta_rev = self.build_context.blocks.first_mut().unwrap();
+        let block_rev = self.build_context.blocks_meta_data.first_mut().unwrap();
+        block_rev.rows.push(Arc::new(row));
+        block_meta_rev.row_count += 1;
         self
     }
 
@@ -59,10 +60,10 @@ fn check_rows(fields: &[FieldRevision], rows: &[RowRevision]) -> CollaborateResu
 
 #[cfg(test)]
 mod tests {
-
     use crate::client_grid::{make_block_meta_delta, make_grid_delta, GridBuilder};
     use flowy_grid_data_model::entities::FieldType;
-    use flowy_grid_data_model::revision::{FieldRevision, GridBlockRevisionData, GridRevision};
+    use flowy_grid_data_model::revision::{FieldRevision, GridBlockRevision, GridRevision};
+    use std::sync::Arc;
 
     #[test]
     fn create_default_grid_test() {
@@ -78,7 +79,7 @@ mod tests {
         let grid_rev = GridRevision {
             grid_id,
             fields: build_context.field_revs,
-            blocks: build_context.blocks,
+            blocks: build_context.blocks.into_iter().map(Arc::new).collect(),
             setting: Default::default(),
         };
 
@@ -86,6 +87,6 @@ mod tests {
         let _: GridRevision = serde_json::from_str(&grid_meta_delta.to_str().unwrap()).unwrap();
 
         let grid_block_meta_delta = make_block_meta_delta(build_context.blocks_meta_data.first().unwrap());
-        let _: GridBlockRevisionData = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
+        let _: GridBlockRevision = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
     }
 }

+ 87 - 37
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -5,8 +5,9 @@ use bytes::Bytes;
 use flowy_grid_data_model::entities::{FieldChangesetParams, FieldOrder};
 use flowy_grid_data_model::entities::{FieldType, GridSettingChangesetParams};
 use flowy_grid_data_model::revision::{
-    gen_block_id, gen_grid_id, FieldRevision, GridBlockRevision, GridBlockRevisionChangeset, GridFilterRevision,
-    GridGroupRevision, GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
+    gen_block_id, gen_grid_filter_id, gen_grid_group_id, gen_grid_id, gen_grid_sort_id, FieldRevision,
+    GridBlockMetaRevision, GridBlockMetaRevisionChangeset, GridFilterRevision, GridGroupRevision, GridLayoutRevision,
+    GridRevision, GridSettingRevision, GridSortRevision,
 };
 use lib_infra::util::move_vec_element;
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
@@ -26,7 +27,7 @@ pub trait JsonDeserializer {
 }
 
 impl GridRevisionPad {
-    pub async fn duplicate_grid_meta(&self) -> (Vec<FieldRevision>, Vec<GridBlockRevision>) {
+    pub async fn duplicate_grid_block_meta(&self) -> (Vec<FieldRevision>, Vec<GridBlockMetaRevision>) {
         let fields = self.grid_rev.fields.to_vec();
 
         let blocks = self
@@ -34,11 +35,11 @@ impl GridRevisionPad {
             .blocks
             .iter()
             .map(|block| {
-                let mut duplicated_block = block.clone();
+                let mut duplicated_block = (&*block.clone()).clone();
                 duplicated_block.block_id = gen_block_id();
                 duplicated_block
             })
-            .collect::<Vec<GridBlockRevision>>();
+            .collect::<Vec<GridBlockMetaRevision>>();
 
         (fields, blocks)
     }
@@ -280,14 +281,14 @@ impl GridRevisionPad {
         }
     }
 
-    pub fn create_block_rev(&mut self, block: GridBlockRevision) -> CollaborateResult<Option<GridChangeset>> {
+    pub fn create_block_meta_rev(&mut self, block: GridBlockMetaRevision) -> CollaborateResult<Option<GridChangeset>> {
         self.modify_grid(|grid_meta| {
             if grid_meta.blocks.iter().any(|b| b.block_id == block.block_id) {
                 tracing::warn!("Duplicate grid block");
                 Ok(None)
             } else {
                 match grid_meta.blocks.last() {
-                    None => grid_meta.blocks.push(block),
+                    None => grid_meta.blocks.push(Arc::new(block)),
                     Some(last_block) => {
                         if last_block.start_row_index > block.start_row_index
                             && last_block.len() > block.start_row_index
@@ -295,7 +296,7 @@ impl GridRevisionPad {
                             let msg = "GridBlock's start_row_index should be greater than the last_block's start_row_index and its len".to_string();
                             return Err(CollaborateError::internal().context(msg))
                         }
-                        grid_meta.blocks.push(block);
+                        grid_meta.blocks.push(Arc::new(block));
                     }
                 }
                 Ok(Some(()))
@@ -303,13 +304,13 @@ impl GridRevisionPad {
         })
     }
 
-    pub fn get_block_revs(&self) -> Vec<GridBlockRevision> {
+    pub fn get_block_meta_revs(&self) -> Vec<Arc<GridBlockMetaRevision>> {
         self.grid_rev.blocks.clone()
     }
 
     pub fn update_block_rev(
         &mut self,
-        changeset: GridBlockRevisionChangeset,
+        changeset: GridBlockMetaRevisionChangeset,
     ) -> CollaborateResult<Option<GridChangeset>> {
         let block_id = changeset.block_id.clone();
         self.modify_block(&block_id, |block| {
@@ -329,8 +330,13 @@ impl GridRevisionPad {
         })
     }
 
-    pub fn get_grid_setting_rev(&self) -> GridSettingRevision {
-        self.grid_rev.setting.clone()
+    pub fn get_grid_setting_rev(&self) -> &GridSettingRevision {
+        &self.grid_rev.setting
+    }
+
+    pub fn get_filters(&self, layout: Option<&GridLayoutRevision>) -> Option<&Vec<GridFilterRevision>> {
+        let layout_ty = layout.unwrap_or(&self.grid_rev.setting.layout);
+        self.grid_rev.setting.filters.get(layout_ty)
     }
 
     pub fn update_grid_setting_rev(
@@ -341,37 +347,78 @@ impl GridRevisionPad {
             let mut is_changed = None;
             let layout_rev: GridLayoutRevision = changeset.layout_type.into();
 
-            if let Some(filter) = changeset.filter {
-                grid_rev.setting.filter.insert(
-                    layout_rev.clone(),
-                    GridFilterRevision {
-                        field_id: filter.field_id,
-                    },
-                );
+            if let Some(params) = changeset.insert_filter {
+                let rev = GridFilterRevision {
+                    id: gen_grid_filter_id(),
+                    field_id: params.field_id,
+                    condition: params.condition,
+                    content: params.content,
+                };
+
+                grid_rev
+                    .setting
+                    .filters
+                    .entry(layout_rev.clone())
+                    .or_insert_with(std::vec::Vec::new)
+                    .push(rev);
+
                 is_changed = Some(())
             }
+            if let Some(delete_filter_id) = changeset.delete_filter {
+                match grid_rev.setting.filters.get_mut(&layout_rev) {
+                    Some(filters) => filters.retain(|filter| filter.id != delete_filter_id),
+                    None => {
+                        tracing::warn!("Can't find the filter with {:?}", layout_rev);
+                    }
+                }
+            }
+            if let Some(params) = changeset.insert_group {
+                let rev = GridGroupRevision {
+                    id: gen_grid_group_id(),
+                    field_id: params.field_id,
+                    sub_field_id: params.sub_field_id,
+                };
+
+                grid_rev
+                    .setting
+                    .groups
+                    .entry(layout_rev.clone())
+                    .or_insert_with(std::vec::Vec::new)
+                    .push(rev);
 
-            if let Some(group) = changeset.group {
-                grid_rev.setting.group.insert(
-                    layout_rev.clone(),
-                    GridGroupRevision {
-                        group_field_id: group.group_field_id,
-                        sub_group_field_id: group.sub_group_field_id,
-                    },
-                );
                 is_changed = Some(())
             }
-
-            if let Some(sort) = changeset.sort {
-                grid_rev.setting.sort.insert(
-                    layout_rev,
-                    GridSortRevision {
-                        field_id: sort.field_id,
-                    },
-                );
+            if let Some(delete_group_id) = changeset.delete_group {
+                match grid_rev.setting.groups.get_mut(&layout_rev) {
+                    Some(groups) => groups.retain(|group| group.id != delete_group_id),
+                    None => {
+                        tracing::warn!("Can't find the group with {:?}", layout_rev);
+                    }
+                }
+            }
+            if let Some(sort) = changeset.insert_sort {
+                let rev = GridSortRevision {
+                    id: gen_grid_sort_id(),
+                    field_id: sort.field_id,
+                };
+
+                grid_rev
+                    .setting
+                    .sorts
+                    .entry(layout_rev.clone())
+                    .or_insert_with(std::vec::Vec::new)
+                    .push(rev);
                 is_changed = Some(())
             }
 
+            if let Some(delete_sort_id) = changeset.delete_sort {
+                match grid_rev.setting.sorts.get_mut(&layout_rev) {
+                    Some(sorts) => sorts.retain(|sort| sort.id != delete_sort_id),
+                    None => {
+                        tracing::warn!("Can't find the sort with {:?}", layout_rev);
+                    }
+                }
+            }
             Ok(is_changed)
         })
     }
@@ -415,7 +462,7 @@ impl GridRevisionPad {
 
     fn modify_block<F>(&mut self, block_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
     where
-        F: FnOnce(&mut GridBlockRevision) -> CollaborateResult<Option<()>>,
+        F: FnOnce(&mut GridBlockMetaRevision) -> CollaborateResult<Option<()>>,
     {
         self.modify_grid(
             |grid_rev| match grid_rev.blocks.iter().position(|block| block.block_id == block_id) {
@@ -423,7 +470,10 @@ impl GridRevisionPad {
                     tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id);
                     Ok(None)
                 }
-                Some(index) => f(&mut grid_rev.blocks[index]),
+                Some(index) => {
+                    let block_rev = Arc::make_mut(&mut grid_rev.blocks[index]);
+                    f(block_rev)
+                }
             },
         )
     }

+ 4 - 6
shared-lib/flowy-user-data-model/src/entities/user_setting.rs

@@ -20,10 +20,12 @@ pub struct AppearanceSettings {
     pub locale: LocaleSettings,
 
     #[pb(index = 3)]
-    #[serde(default = "reset_default_value")]
+    #[serde(default = "DEFAULT_RESET_VALUE")]
     pub reset_as_default: bool,
 }
 
+const DEFAULT_RESET_VALUE: fn() -> bool = || APPEARANCE_RESET_AS_DEFAULT;
+
 #[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone)]
 pub struct LocaleSettings {
     #[pb(index = 1)]
@@ -42,12 +44,8 @@ impl std::default::Default for LocaleSettings {
     }
 }
 
-fn reset_default_value() -> bool {
-    APPEARANCE_RESET_AS_DEFAULT
-}
-
 pub const APPEARANCE_DEFAULT_THEME: &str = "light";
-pub const APPEARANCE_RESET_AS_DEFAULT: bool = true;
+const APPEARANCE_RESET_AS_DEFAULT: bool = true;
 
 impl std::default::Default for AppearanceSettings {
     fn default() -> Self {