Explorar el Código

Merge pull request #1570 from AppFlowy-IO/feat/grid_sort

Feat/grid sort
Nathan.fooo hace 2 años
padre
commit
9666269e27
Se han modificado 100 ficheros con 1674 adiciones y 1187 borrados
  1. 1 3
      frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
  2. 30 51
      frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart
  3. 1 2
      frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart
  4. 1 1
      frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart
  5. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
  6. 0 53
      frontend/app_flowy/lib/plugins/grid/application/block/block_listener.dart
  7. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/grid_listener.dart
  8. 2 2
      frontend/app_flowy/lib/plugins/grid/application/filter/checkbox_filter_editor_bloc.dart
  9. 2 2
      frontend/app_flowy/lib/plugins/grid/application/filter/checklist_filter_bloc.dart
  10. 8 8
      frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart
  11. 11 9
      frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart
  12. 2 2
      frontend/app_flowy/lib/plugins/grid/application/filter/select_option_filter_bloc.dart
  13. 2 2
      frontend/app_flowy/lib/plugins/grid/application/filter/text_filter_editor_bloc.dart
  14. 1 4
      frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
  15. 15 49
      frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart
  16. 0 1
      frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
  17. 1 4
      frontend/app_flowy/lib/plugins/grid/application/row/row_action_sheet_bloc.dart
  18. 1 4
      frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart
  19. 10 7
      frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart
  20. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_list.dart
  21. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_listener.dart
  22. 0 6
      frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart
  23. 22 18
      frontend/app_flowy/lib/plugins/grid/application/view/grid_view_cache.dart
  24. 72 0
      frontend/app_flowy/lib/plugins/grid/application/view/grid_view_listener.dart
  25. 9 9
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart
  26. 8 8
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart
  27. 12 12
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/condition_list.dart
  28. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart
  29. 25 25
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart
  30. 2 11
      frontend/app_flowy/test/bloc_test/board_test/util.dart
  31. 7 7
      frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart
  32. 1 1
      frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart
  33. 4 4
      frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart
  34. 2 2
      frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_by_checkbox_test.dart
  35. 10 8
      frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_by_text_test.dart
  36. 2 9
      frontend/app_flowy/test/bloc_test/grid_test/util.dart
  37. 4 2
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  38. 0 245
      frontend/rust-lib/flowy-grid/src/entities/block_entities.rs
  39. 11 10
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/checkbox_filter.rs
  40. 11 11
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs
  41. 19 19
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs
  42. 16 16
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/number_filter.rs
  43. 12 12
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs
  44. 16 16
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs
  45. 31 4
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs
  46. 2 2
      frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs
  47. 4 2
      frontend/rust-lib/flowy-grid/src/entities/mod.rs
  48. 130 6
      frontend/rust-lib/flowy-grid/src/entities/row_entities.rs
  49. 25 4
      frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs
  50. 153 0
      frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
  51. 64 0
      frontend/rust-lib/flowy-grid/src/entities/view_entities.rs
  52. 12 16
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  53. 1 8
      frontend/rust-lib/flowy-grid/src/event_map.rs
  54. 4 21
      frontend/rust-lib/flowy-grid/src/services/block_editor.rs
  55. 71 62
      frontend/rust-lib/flowy-grid/src/services/block_manager.rs
  56. 54 43
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  57. 2 2
      frontend/rust-lib/flowy-grid/src/services/cell/mod.rs
  58. 5 5
      frontend/rust-lib/flowy-grid/src/services/cell/type_cell_data.rs
  59. 11 11
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_filter.rs
  60. 8 8
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs
  61. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs
  62. 21 21
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs
  63. 10 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs
  64. 18 18
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_filter.rs
  65. 8 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  66. 1 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs
  67. 3 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs
  68. 3 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs
  69. 3 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs
  70. 27 27
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs
  71. 15 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs
  72. 3 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs
  73. 3 3
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs
  74. 20 20
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_filter.rs
  75. 14 12
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs
  76. 5 5
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_filter.rs
  77. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs
  78. 16 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs
  79. 25 7
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs
  80. 20 23
      frontend/rust-lib/flowy-grid/src/services/filter/controller.rs
  81. 3 4
      frontend/rust-lib/flowy-grid/src/services/filter/entities.rs
  82. 34 25
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  83. 4 4
      frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs
  84. 5 5
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  85. 1 0
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  86. 1 1
      frontend/rust-lib/flowy-grid/src/services/row/mod.rs
  87. 10 41
      frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs
  88. 2 0
      frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs
  89. 58 0
      frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
  90. 67 0
      frontend/rust-lib/flowy-grid/src/services/sort/entities.rs
  91. 7 0
      frontend/rust-lib/flowy-grid/src/services/sort/mod.rs
  92. 30 0
      frontend/rust-lib/flowy-grid/src/services/sort/task.rs
  93. 9 7
      frontend/rust-lib/flowy-grid/src/services/view_editor/changed_notifier.rs
  94. 164 12
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs
  95. 59 9
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs
  96. 32 6
      frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs
  97. 20 8
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  98. 3 3
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/checkbox_filter_test.rs
  99. 3 3
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs
  100. 6 6
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs

+ 1 - 3
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -1,7 +1,6 @@
 import 'dart:async';
 import 'dart:collection';
 
-import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
@@ -229,8 +228,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   }
 
   GridRowCache? getRowCache(String blockId) {
-    final GridBlockCache? blockCache = _gridDataController.blocks[blockId];
-    return blockCache?.rowCache;
+    return _gridDataController.rowCache;
   }
 
   void _startListening() {

+ 30 - 51
frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart

@@ -1,6 +1,6 @@
 import 'dart:collection';
 
-import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
+import 'package:app_flowy/plugins/grid/application/view/grid_view_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/grid_service.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
@@ -31,11 +31,7 @@ class BoardDataController {
   final GridFFIService _gridFFIService;
   final GridFieldController fieldController;
   final BoardListener _listener;
-
-  // key: the block id
-  final LinkedHashMap<String, GridBlockCache> _blocks;
-  UnmodifiableMapView<String, GridBlockCache> get blocks =>
-      UnmodifiableMapView(_blocks);
+  late GridViewCache _viewCache;
 
   OnFieldsChanged? _onFieldsChanged;
   OnGridChanged? _onGridChanged;
@@ -43,21 +39,23 @@ class BoardDataController {
   OnRowsChanged? _onRowsChanged;
   OnError? _onError;
 
-  List<RowInfo> get rowInfos {
-    final List<RowInfo> rows = [];
-    for (var block in _blocks.values) {
-      rows.addAll(block.rows);
-    }
-    return rows;
-  }
+  List<RowInfo> get rowInfos => _viewCache.rowInfos;
+  GridRowCache get rowCache => _viewCache.rowCache;
 
   BoardDataController({required ViewPB view})
       : gridId = view.id,
         _listener = BoardListener(view.id),
-        // ignore: prefer_collection_literals
-        _blocks = LinkedHashMap(),
         _gridFFIService = GridFFIService(gridId: view.id),
-        fieldController = GridFieldController(gridId: view.id);
+        fieldController = GridFieldController(gridId: view.id) {
+    //
+    _viewCache = GridViewCache(
+      gridId: view.id,
+      fieldController: fieldController,
+    );
+    _viewCache.addListener(onRowsChanged: (reason) {
+      _onRowsChanged?.call(rowInfos, reason);
+    });
+  }
 
   void addListener({
     required OnGridChanged onGridChanged,
@@ -110,23 +108,21 @@ class BoardDataController {
 
   Future<Either<Unit, FlowyError>> openGrid() async {
     final result = await _gridFFIService.openGrid();
-    return Future(
-      () => result.fold(
-        (grid) async {
-          _onGridChanged?.call(grid);
-          final result = await fieldController.loadFields(
-            fieldIds: grid.fields,
-          );
-          return result.fold(
-            (l) {
-              _loadGroups(grid.blocks);
-              return left(l);
-            },
-            (err) => right(err),
-          );
-        },
-        (err) => right(err),
-      ),
+
+    return result.fold(
+      (grid) async {
+        _onGridChanged?.call(grid);
+        final result = await fieldController.loadFields(fieldIds: grid.fields);
+        return result.fold(
+          (l) {
+            _loadGroups();
+            _viewCache.rowCache.initializeRows(grid.rows);
+            return left(l);
+          },
+          (err) => right(err),
+        );
+      },
+      (err) => right(err),
     );
   }
 
@@ -138,26 +134,9 @@ class BoardDataController {
   Future<void> dispose() async {
     await _gridFFIService.closeGrid();
     await fieldController.dispose();
-
-    for (final blockCache in _blocks.values) {
-      blockCache.dispose();
-    }
   }
 
-  Future<void> _loadGroups(List<BlockPB> blocks) async {
-    for (final block in blocks) {
-      final cache = GridBlockCache(
-        gridId: gridId,
-        block: block,
-        fieldController: fieldController,
-      );
-
-      cache.addListener(onRowsChanged: (reason) {
-        _onRowsChanged?.call(rowInfos, reason);
-      });
-      _blocks[block.id] = cache;
-    }
-
+  Future<void> _loadGroups() async {
     final result = await _gridFFIService.loadGroups();
     return Future(
       () => result.fold(

+ 1 - 2
frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart

@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
 import 'package:equatable/equatable.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -23,7 +23,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
     required bool isEditing,
   })  : _rowService = RowFFIService(
           gridId: gridId,
-          blockId: dataController.rowPB.blockId,
         ),
         _dataController = dataController,
         super(

+ 1 - 1
frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart

@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 
 typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -16,8 +16,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import '../../grid/application/row/row_cache.dart';

+ 0 - 53
frontend/app_flowy/lib/plugins/grid/application/block/block_listener.dart

@@ -1,53 +0,0 @@
-import 'dart:async';
-import 'dart:typed_data';
-import 'package:app_flowy/core/grid_notification.dart';
-import 'package:dartz/dartz.dart';
-import 'package:flowy_infra/notifier.dart';
-import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
-
-typedef GridBlockUpdateNotifierValue = Either<GridBlockChangesetPB, 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(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
-    switch (ty) {
-      case GridDartNotification.DidUpdateGridBlock:
-        result.fold(
-          (payload) => _rowsUpdateNotifier?.value =
-              left(GridBlockChangesetPB.fromBuffer(payload)),
-          (error) => _rowsUpdateNotifier?.value = right(error),
-        );
-        break;
-
-      default:
-        break;
-    }
-  }
-
-  Future<void> stop() async {
-    await _listener?.stop();
-    _rowsUpdateNotifier?.dispose();
-    _rowsUpdateNotifier = null;
-  }
-}

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/grid_listener.dart

@@ -27,7 +27,7 @@ class GridFieldsListener {
 
   void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
     switch (ty) {
-      case GridDartNotification.DidUpdateGridField:
+      case GridDartNotification.DidUpdateGridFields:
         result.fold(
           (payload) => updateFieldsNotifier?.value =
               left(GridFieldChangesetPB.fromBuffer(payload)),

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/filter/checkbox_filter_editor_bloc.dart

@@ -28,7 +28,7 @@ class CheckboxFilterEditorBloc
           initial: () async {
             _startListening();
           },
-          updateCondition: (CheckboxFilterCondition condition) {
+          updateCondition: (CheckboxFilterConditionPB condition) {
             _ffiService.insertCheckboxFilter(
               filterId: filterInfo.filter.id,
               fieldId: filterInfo.fieldInfo.id,
@@ -79,7 +79,7 @@ class CheckboxFilterEditorEvent with _$CheckboxFilterEditorEvent {
   const factory CheckboxFilterEditorEvent.didReceiveFilter(FilterPB filter) =
       _DidReceiveFilter;
   const factory CheckboxFilterEditorEvent.updateCondition(
-      CheckboxFilterCondition condition) = _UpdateCondition;
+      CheckboxFilterConditionPB condition) = _UpdateCondition;
   const factory CheckboxFilterEditorEvent.delete() = _Delete;
 }
 

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/filter/checklist_filter_bloc.dart

@@ -32,7 +32,7 @@ class ChecklistFilterEditorBloc
           initial: () async {
             _startListening();
           },
-          updateCondition: (ChecklistFilterCondition condition) {
+          updateCondition: (ChecklistFilterConditionPB condition) {
             _ffiService.insertChecklistFilter(
               filterId: filterInfo.filter.id,
               fieldId: filterInfo.fieldInfo.id,
@@ -85,7 +85,7 @@ class ChecklistFilterEditorEvent with _$ChecklistFilterEditorEvent {
   const factory ChecklistFilterEditorEvent.didReceiveFilter(FilterPB filter) =
       _DidReceiveFilter;
   const factory ChecklistFilterEditorEvent.updateCondition(
-      ChecklistFilterCondition condition) = _UpdateCondition;
+      ChecklistFilterConditionPB condition) = _UpdateCondition;
   const factory ChecklistFilterEditorEvent.delete() = _Delete;
 }
 

+ 8 - 8
frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart

@@ -90,48 +90,48 @@ class GridCreateFilterBloc
       case FieldType.Checkbox:
         return _ffiService.insertCheckboxFilter(
           fieldId: fieldId,
-          condition: CheckboxFilterCondition.IsChecked,
+          condition: CheckboxFilterConditionPB.IsChecked,
         );
       case FieldType.DateTime:
         final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
         return _ffiService.insertDateFilter(
           fieldId: fieldId,
-          condition: DateFilterCondition.DateIs,
+          condition: DateFilterConditionPB.DateIs,
           timestamp: timestamp,
         );
       case FieldType.MultiSelect:
         return _ffiService.insertSelectOptionFilter(
           fieldId: fieldId,
-          condition: SelectOptionCondition.OptionIs,
+          condition: SelectOptionConditionPB.OptionIs,
           fieldType: FieldType.MultiSelect,
         );
       case FieldType.Checklist:
         return _ffiService.insertChecklistFilter(
           fieldId: fieldId,
-          condition: ChecklistFilterCondition.IsIncomplete,
+          condition: ChecklistFilterConditionPB.IsIncomplete,
         );
       case FieldType.Number:
         return _ffiService.insertNumberFilter(
           fieldId: fieldId,
-          condition: NumberFilterCondition.Equal,
+          condition: NumberFilterConditionPB.Equal,
           content: "",
         );
       case FieldType.RichText:
         return _ffiService.insertTextFilter(
           fieldId: fieldId,
-          condition: TextFilterCondition.Contains,
+          condition: TextFilterConditionPB.Contains,
           content: '',
         );
       case FieldType.SingleSelect:
         return _ffiService.insertSelectOptionFilter(
           fieldId: fieldId,
-          condition: SelectOptionCondition.OptionIs,
+          condition: SelectOptionConditionPB.OptionIs,
           fieldType: FieldType.SingleSelect,
         );
       case FieldType.URL:
         return _ffiService.insertURLFilter(
           fieldId: fieldId,
-          condition: TextFilterCondition.Contains,
+          condition: TextFilterConditionPB.Contains,
         );
     }
 

+ 11 - 9
frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart

@@ -32,7 +32,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertTextFilter({
     required String fieldId,
     String? filterId,
-    required TextFilterCondition condition,
+    required TextFilterConditionPB condition,
     required String content,
   }) {
     final filter = TextFilterPB()
@@ -50,7 +50,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertCheckboxFilter({
     required String fieldId,
     String? filterId,
-    required CheckboxFilterCondition condition,
+    required CheckboxFilterConditionPB condition,
   }) {
     final filter = CheckboxFilterPB()..condition = condition;
 
@@ -65,7 +65,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertNumberFilter({
     required String fieldId,
     String? filterId,
-    required NumberFilterCondition condition,
+    required NumberFilterConditionPB condition,
     String content = "",
   }) {
     final filter = NumberFilterPB()
@@ -83,7 +83,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertDateFilter({
     required String fieldId,
     String? filterId,
-    required DateFilterCondition condition,
+    required DateFilterConditionPB condition,
     int? start,
     int? end,
     int? timestamp,
@@ -112,7 +112,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertURLFilter({
     required String fieldId,
     String? filterId,
-    required TextFilterCondition condition,
+    required TextFilterConditionPB condition,
     String content = "",
   }) {
     final filter = TextFilterPB()
@@ -130,7 +130,7 @@ class FilterFFIService {
   Future<Either<Unit, FlowyError>> insertSelectOptionFilter({
     required String fieldId,
     required FieldType fieldType,
-    required SelectOptionCondition condition,
+    required SelectOptionConditionPB condition,
     String? filterId,
     List<String> optionIds = const [],
   }) {
@@ -148,7 +148,7 @@ class FilterFFIService {
 
   Future<Either<Unit, FlowyError>> insertChecklistFilter({
     required String fieldId,
-    required ChecklistFilterCondition condition,
+    required ChecklistFilterConditionPB condition,
     String? filterId,
     List<String> optionIds = const [],
   }) {
@@ -171,6 +171,7 @@ class FilterFFIService {
     var insertFilterPayload = AlterFilterPayloadPB.create()
       ..fieldId = fieldId
       ..fieldType = fieldType
+      ..viewId = viewId
       ..data = data;
 
     if (filterId != null) {
@@ -179,7 +180,7 @@ class FilterFFIService {
 
     final payload = GridSettingChangesetPB.create()
       ..gridId = viewId
-      ..insertFilter = insertFilterPayload;
+      ..alterFilter = insertFilterPayload;
     return GridEventUpdateGridSetting(payload).send().then((result) {
       return result.fold(
         (l) => left(l),
@@ -196,11 +197,12 @@ class FilterFFIService {
     required String filterId,
     required FieldType fieldType,
   }) {
-    TextFilterCondition.DoesNotContain.value;
+    TextFilterConditionPB.DoesNotContain.value;
 
     final deleteFilterPayload = DeleteFilterPayloadPB.create()
       ..fieldId = fieldId
       ..filterId = filterId
+      ..viewId = viewId
       ..fieldType = fieldType;
 
     final payload = GridSettingChangesetPB.create()

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/filter/select_option_filter_bloc.dart

@@ -33,7 +33,7 @@ class SelectOptionFilterEditorBloc
             _startListening();
             _loadOptions();
           },
-          updateCondition: (SelectOptionCondition condition) {
+          updateCondition: (SelectOptionConditionPB condition) {
             _ffiService.insertSelectOptionFilter(
               filterId: filterInfo.filter.id,
               fieldId: filterInfo.fieldInfo.id,
@@ -114,7 +114,7 @@ class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent {
   const factory SelectOptionFilterEditorEvent.didReceiveFilter(
       FilterPB filter) = _DidReceiveFilter;
   const factory SelectOptionFilterEditorEvent.updateCondition(
-      SelectOptionCondition condition) = _UpdateCondition;
+      SelectOptionConditionPB condition) = _UpdateCondition;
   const factory SelectOptionFilterEditorEvent.updateContent(
       List<String> optionIds) = _UpdateContent;
   const factory SelectOptionFilterEditorEvent.updateFilterDescription(

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/filter/text_filter_editor_bloc.dart

@@ -28,7 +28,7 @@ class TextFilterEditorBloc
           initial: () async {
             _startListening();
           },
-          updateCondition: (TextFilterCondition condition) {
+          updateCondition: (TextFilterConditionPB condition) {
             _ffiService.insertTextFilter(
               filterId: filterInfo.filter.id,
               fieldId: filterInfo.fieldInfo.id,
@@ -88,7 +88,7 @@ class TextFilterEditorEvent with _$TextFilterEditorEvent {
   const factory TextFilterEditorEvent.didReceiveFilter(FilterPB filter) =
       _DidReceiveFilter;
   const factory TextFilterEditorEvent.updateCondition(
-      TextFilterCondition condition) = _UpdateCondition;
+      TextFilterConditionPB condition) = _UpdateCondition;
   const factory TextFilterEditorEvent.updateContent(String content) =
       _UpdateContent;
   const factory TextFilterEditorEvent.delete() = _Delete;

+ 1 - 4
frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart

@@ -6,7 +6,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'block/block_cache.dart';
 import 'field/field_controller.dart';
 import 'grid_data_controller.dart';
 import 'row/row_cache.dart';
@@ -38,7 +37,6 @@ class GridBloc extends Bloc<GridEvent, GridState> {
           },
           deleteRow: (rowInfo) async {
             final rowService = RowFFIService(
-              blockId: rowInfo.rowPB.blockId,
               gridId: rowInfo.gridId,
             );
             await rowService.deleteRow(rowInfo.rowPB.id);
@@ -70,8 +68,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   }
 
   GridRowCache? getRowCache(String blockId, String rowId) {
-    final GridBlockCache? blockCache = gridController.blocks[blockId];
-    return blockCache?.rowCache;
+    return gridController.rowCache;
   }
 
   void _startListening() {

+ 15 - 49
frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart

@@ -1,14 +1,10 @@
-import 'dart:collection';
-
 import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'dart:async';
 import 'package:dartz/dartz.dart';
-import 'block/block_cache.dart';
+import 'view/grid_view_cache.dart';
 import 'field/field_controller.dart';
 import 'prelude.dart';
 import 'row/row_cache.dart';
@@ -27,29 +23,25 @@ class GridController {
   final String gridId;
   final GridFFIService _gridFFIService;
   final GridFieldController fieldController;
+  late GridViewCache _viewCache;
+
   OnRowsChanged? _onRowChanged;
   OnGridChanged? _onGridChanged;
-
-  // Getters
-  // key: the block id
-  final LinkedHashMap<String, GridBlockCache> _blocks;
-  UnmodifiableMapView<String, GridBlockCache> get blocks =>
-      UnmodifiableMapView(_blocks);
-
-  List<RowInfo> get rowInfos {
-    final List<RowInfo> rows = [];
-    for (var block in _blocks.values) {
-      rows.addAll(block.rows);
-    }
-    return rows;
-  }
+  List<RowInfo> get rowInfos => _viewCache.rowInfos;
+  GridRowCache get rowCache => _viewCache.rowCache;
 
   GridController({required ViewPB view})
       : gridId = view.id,
-        // ignore: prefer_collection_literals
-        _blocks = LinkedHashMap(),
         _gridFFIService = GridFFIService(gridId: view.id),
-        fieldController = GridFieldController(gridId: view.id);
+        fieldController = GridFieldController(gridId: view.id) {
+    _viewCache = GridViewCache(
+      gridId: gridId,
+      fieldController: fieldController,
+    );
+    _viewCache.addListener(onRowsChanged: (reason) {
+      _onRowChanged?.call(rowInfos, reason);
+    });
+  }
 
   void addListener({
     OnGridChanged? onGridChanged,
@@ -71,9 +63,8 @@ class GridController {
     return _gridFFIService.openGrid().then((result) {
       return result.fold(
         (grid) async {
-          _initialBlocks(grid.blocks);
           _onGridChanged?.call(grid);
-
+          _viewCache.rowCache.initializeRows(grid.rows);
           final result = await fieldController.loadFields(
             fieldIds: grid.fields,
           );
@@ -91,30 +82,5 @@ class GridController {
   Future<void> dispose() async {
     await _gridFFIService.closeGrid();
     await fieldController.dispose();
-
-    for (final blockCache in _blocks.values) {
-      blockCache.dispose();
-    }
-  }
-
-  void _initialBlocks(List<BlockPB> blocks) {
-    for (final block in blocks) {
-      if (_blocks[block.id] != null) {
-        Log.warn("Initial duplicate block's cache: ${block.id}");
-        return;
-      }
-
-      final cache = GridBlockCache(
-        gridId: gridId,
-        block: block,
-        fieldController: fieldController,
-      );
-
-      cache.addListener(onRowsChanged: (reason) {
-        _onRowChanged?.call(rowInfos, reason);
-      });
-
-      _blocks[block.id] = cache;
-    }
   }
 }

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

@@ -2,7 +2,6 @@ import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';

+ 1 - 4
frontend/app_flowy/lib/plugins/grid/application/row/row_action_sheet_bloc.dart

@@ -15,10 +15,7 @@ class RowActionSheetBloc
   final RowFFIService _rowService;
 
   RowActionSheetBloc({required RowInfo rowInfo})
-      : _rowService = RowFFIService(
-          gridId: rowInfo.gridId,
-          blockId: rowInfo.rowPB.blockId,
-        ),
+      : _rowService = RowFFIService(gridId: rowInfo.gridId),
         super(RowActionSheetState.initial(rowInfo)) {
     on<RowActionSheetEvent>(
       (event, emit) async {

+ 1 - 4
frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart

@@ -18,10 +18,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
   RowBloc({
     required RowInfo rowInfo,
     required GridRowDataController dataController,
-  })  : _rowService = RowFFIService(
-          gridId: rowInfo.gridId,
-          blockId: rowInfo.rowPB.blockId,
-        ),
+  })  : _rowService = RowFFIService(gridId: rowInfo.gridId),
         _dataController = dataController,
         super(RowState.initial(rowInfo, dataController.loadData())) {
     on<RowEvent>(

+ 10 - 7
frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart

@@ -3,8 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
@@ -27,7 +26,7 @@ abstract class IGridRowFieldNotifier {
 
 class GridRowCache {
   final String gridId;
-  final BlockPB block;
+  final List<RowPB> rows;
 
   /// _rows containers the current block's rows
   /// Use List to reverse the order of the GridRow.
@@ -46,7 +45,7 @@ class GridRowCache {
 
   GridRowCache({
     required this.gridId,
-    required this.block,
+    required this.rows,
     required IGridRowFieldNotifier notifier,
   })  : _cellCache = GridCellCache(gridId: gridId),
         _rowChangeReasonNotifier = _RowChangesetNotifier(),
@@ -56,8 +55,10 @@ class GridRowCache {
         .receive(const RowsChangedReason.fieldDidChange()));
     notifier.onRowFieldChanged(
         (field) => _cellCache.removeCellWithFieldId(field.id));
+  }
 
-    for (final row in block.rows) {
+  void initializeRows(List<RowPB> rows) {
+    for (final row in rows) {
       final rowInfo = buildGridRow(row);
       _rowList.add(rowInfo);
     }
@@ -69,10 +70,13 @@ class GridRowCache {
     await _cellCache.dispose();
   }
 
-  void applyChangesets(GridBlockChangesetPB changeset) {
+  void applyRowsChanged(GridViewRowsChangesetPB changeset) {
     _deleteRows(changeset.deletedRows);
     _insertRows(changeset.insertedRows);
     _updateRows(changeset.updatedRows);
+  }
+
+  void applyRowsVisibility(GridRowsVisibilityChangesetPB changeset) {
     _hideRows(changeset.invisibleRows);
     _showRows(changeset.visibleRows);
   }
@@ -192,7 +196,6 @@ class GridRowCache {
   Future<void> _loadRow(String rowId) async {
     final payload = RowIdPB.create()
       ..gridId = gridId
-      ..blockId = block.id
       ..rowId = rowId;
 
     final result = await GridEventGetRow(payload).send();

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_list.dart

@@ -1,6 +1,6 @@
 import 'dart:collection';
 
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 
 import 'row_cache.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_listener.dart

@@ -1,12 +1,12 @@
 import 'package:app_flowy/core/grid_notification.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
 import 'package:flowy_infra/notifier.dart';
 import 'dart:async';
 import 'dart:typed_data';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 
 typedef UpdateRowNotifiedValue = Either<RowPB, FlowyError>;
 typedef UpdateFieldNotifiedValue = Either<List<FieldPB>, FlowyError>;

+ 0 - 6
frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart

@@ -1,18 +1,15 @@
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 
 class RowFFIService {
   final String gridId;
-  final String blockId;
 
   RowFFIService({
     required this.gridId,
-    required this.blockId,
   });
 
   Future<Either<RowPB, FlowyError>> createRow(String rowId) {
@@ -26,7 +23,6 @@ class RowFFIService {
   Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
     final payload = RowIdPB.create()
       ..gridId = gridId
-      ..blockId = blockId
       ..rowId = rowId;
 
     return GridEventGetRow(payload).send();
@@ -35,7 +31,6 @@ class RowFFIService {
   Future<Either<Unit, FlowyError>> deleteRow(String rowId) {
     final payload = RowIdPB.create()
       ..gridId = gridId
-      ..blockId = blockId
       ..rowId = rowId;
 
     return GridEventDeleteRow(payload).send();
@@ -44,7 +39,6 @@ class RowFFIService {
   Future<Either<Unit, FlowyError>> duplicateRow(String rowId) {
     final payload = RowIdPB.create()
       ..gridId = gridId
-      ..blockId = blockId
       ..rowId = rowId;
 
     return GridEventDuplicateRow(payload).send();

+ 22 - 18
frontend/app_flowy/lib/plugins/grid/application/block/block_cache.dart → frontend/app_flowy/lib/plugins/grid/application/view/grid_view_cache.dart

@@ -1,43 +1,47 @@
 import 'dart:async';
+import 'package:app_flowy/plugins/grid/application/view/grid_view_listener.dart';
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 
 import '../field/field_controller.dart';
 import '../row/row_cache.dart';
-import 'block_listener.dart';
 
 /// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
-class GridBlockCache {
+class GridViewCache {
   final String gridId;
-  final BlockPB block;
   late GridRowCache _rowCache;
-  late GridBlockListener _listener;
+  final GridViewListener _gridViewListener;
 
-  List<RowInfo> get rows => _rowCache.visibleRows;
+  List<RowInfo> get rowInfos => _rowCache.visibleRows;
   GridRowCache get rowCache => _rowCache;
 
-  GridBlockCache({
+  GridViewCache({
     required this.gridId,
-    required this.block,
     required GridFieldController fieldController,
-  }) {
+  }) : _gridViewListener = GridViewListener(viewId: gridId) {
     _rowCache = GridRowCache(
       gridId: gridId,
-      block: block,
+      rows: [],
       notifier: GridRowFieldNotifierImpl(fieldController),
     );
 
-    _listener = GridBlockListener(blockId: block.id);
-    _listener.start((result) {
-      result.fold(
-        (changeset) => _rowCache.applyChangesets(changeset),
-        (err) => Log.error(err),
-      );
-    });
+    _gridViewListener.start(
+      onRowsChanged: (result) {
+        result.fold(
+          (changeset) => _rowCache.applyRowsChanged(changeset),
+          (err) => Log.error(err),
+        );
+      },
+      onRowsVisibilityChanged: (result) {
+        result.fold(
+          (changeset) => _rowCache.applyRowsVisibility(changeset),
+          (err) => Log.error(err),
+        );
+      },
+    );
   }
 
   Future<void> dispose() async {
-    await _listener.stop();
+    await _gridViewListener.stop();
     await _rowCache.dispose();
   }
 

+ 72 - 0
frontend/app_flowy/lib/plugins/grid/application/view/grid_view_listener.dart

@@ -0,0 +1,72 @@
+import 'dart:async';
+import 'dart:typed_data';
+import 'package:app_flowy/core/grid_notification.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_infra/notifier.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/view_entities.pb.dart';
+
+typedef GridRowsVisibilityNotifierValue
+    = Either<GridRowsVisibilityChangesetPB, FlowyError>;
+
+typedef GridViewRowsNotifierValue = Either<GridViewRowsChangesetPB, FlowyError>;
+
+class GridViewListener {
+  final String viewId;
+  PublishNotifier<GridViewRowsNotifierValue>? _rowsNotifier = PublishNotifier();
+  PublishNotifier<GridRowsVisibilityNotifierValue>? _rowsVisibilityNotifier =
+      PublishNotifier();
+
+  GridNotificationListener? _listener;
+  GridViewListener({required this.viewId});
+
+  void start({
+    required void Function(GridViewRowsNotifierValue) onRowsChanged,
+    required void Function(GridRowsVisibilityNotifierValue)
+        onRowsVisibilityChanged,
+  }) {
+    if (_listener != null) {
+      _listener?.stop();
+    }
+
+    _listener = GridNotificationListener(
+      objectId: viewId,
+      handler: _handler,
+    );
+
+    _rowsNotifier?.addPublishListener(onRowsChanged);
+    _rowsVisibilityNotifier?.addPublishListener(onRowsVisibilityChanged);
+  }
+
+  void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
+    switch (ty) {
+      case GridDartNotification.DidUpdateGridViewRowsVisibility:
+        result.fold(
+          (payload) => _rowsVisibilityNotifier?.value =
+              left(GridRowsVisibilityChangesetPB.fromBuffer(payload)),
+          (error) => _rowsVisibilityNotifier?.value = right(error),
+        );
+        break;
+      case GridDartNotification.DidUpdateGridViewRows:
+        result.fold(
+          (payload) => _rowsNotifier?.value =
+              left(GridViewRowsChangesetPB.fromBuffer(payload)),
+          (error) => _rowsNotifier?.value = right(error),
+        );
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  Future<void> stop() async {
+    await _listener?.stop();
+    _rowsVisibilityNotifier?.dispose();
+    _rowsVisibilityNotifier = null;
+
+    _rowsNotifier?.dispose();
+    _rowsNotifier = null;
+  }
+}

+ 9 - 9
frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart

@@ -108,7 +108,7 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
         children: [
           FlowyText(state.filterInfo.fieldInfo.name),
           const HSpace(4),
-          CheckboxFilterConditionList(
+          CheckboxFilterConditionPBList(
             filterInfo: state.filterInfo,
             popoverMutex: popoverMutex,
             onCondition: (condition) {
@@ -136,11 +136,11 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
   }
 }
 
-class CheckboxFilterConditionList extends StatelessWidget {
+class CheckboxFilterConditionPBList extends StatelessWidget {
   final FilterInfo filterInfo;
   final PopoverMutex popoverMutex;
-  final Function(CheckboxFilterCondition) onCondition;
-  const CheckboxFilterConditionList({
+  final Function(CheckboxFilterConditionPB) onCondition;
+  const CheckboxFilterConditionPBList({
     required this.filterInfo,
     required this.popoverMutex,
     required this.onCondition,
@@ -154,7 +154,7 @@ class CheckboxFilterConditionList extends StatelessWidget {
       asBarrier: true,
       mutex: popoverMutex,
       direction: PopoverDirection.bottomWithCenterAligned,
-      actions: CheckboxFilterCondition.values
+      actions: CheckboxFilterConditionPB.values
           .map(
             (action) => ConditionWrapper(
               action,
@@ -177,7 +177,7 @@ class CheckboxFilterConditionList extends StatelessWidget {
 }
 
 class ConditionWrapper extends ActionCell {
-  final CheckboxFilterCondition inner;
+  final CheckboxFilterConditionPB inner;
   final bool isSelected;
 
   ConditionWrapper(this.inner, this.isSelected);
@@ -195,12 +195,12 @@ class ConditionWrapper extends ActionCell {
   String get name => inner.filterName;
 }
 
-extension TextFilterConditionExtension on CheckboxFilterCondition {
+extension TextFilterConditionPBExtension on CheckboxFilterConditionPB {
   String get filterName {
     switch (this) {
-      case CheckboxFilterCondition.IsChecked:
+      case CheckboxFilterConditionPB.IsChecked:
         return LocaleKeys.grid_checkboxFilter_isChecked.tr();
-      case CheckboxFilterCondition.IsUnChecked:
+      case CheckboxFilterConditionPB.IsUnChecked:
         return LocaleKeys.grid_checkboxFilter_isUnchecked.tr();
       default:
         return "";

+ 8 - 8
frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart

@@ -92,7 +92,7 @@ class ChecklistState extends State<ChecklistFilterEditor> {
               children: [
                 FlowyText(state.filterInfo.fieldInfo.name),
                 const HSpace(4),
-                ChecklistFilterConditionList(
+                ChecklistFilterConditionPBList(
                   filterInfo: state.filterInfo,
                 ),
                 const Spacer(),
@@ -117,9 +117,9 @@ class ChecklistState extends State<ChecklistFilterEditor> {
   }
 }
 
-class ChecklistFilterConditionList extends StatelessWidget {
+class ChecklistFilterConditionPBList extends StatelessWidget {
   final FilterInfo filterInfo;
-  const ChecklistFilterConditionList({
+  const ChecklistFilterConditionPBList({
     required this.filterInfo,
     Key? key,
   }) : super(key: key);
@@ -130,7 +130,7 @@ class ChecklistFilterConditionList extends StatelessWidget {
     return PopoverActionList<ConditionWrapper>(
       asBarrier: true,
       direction: PopoverDirection.bottomWithCenterAligned,
-      actions: ChecklistFilterCondition.values
+      actions: ChecklistFilterConditionPB.values
           .map((action) => ConditionWrapper(action))
           .toList(),
       buildChild: (controller) {
@@ -150,7 +150,7 @@ class ChecklistFilterConditionList extends StatelessWidget {
 }
 
 class ConditionWrapper extends ActionCell {
-  final ChecklistFilterCondition inner;
+  final ChecklistFilterConditionPB inner;
 
   ConditionWrapper(this.inner);
 
@@ -158,12 +158,12 @@ class ConditionWrapper extends ActionCell {
   String get name => inner.filterName;
 }
 
-extension ChecklistFilterConditionExtension on ChecklistFilterCondition {
+extension ChecklistFilterConditionPBExtension on ChecklistFilterConditionPB {
   String get filterName {
     switch (this) {
-      case ChecklistFilterCondition.IsComplete:
+      case ChecklistFilterConditionPB.IsComplete:
         return LocaleKeys.grid_checklistFilter_isComplete.tr();
-      case ChecklistFilterCondition.IsIncomplete:
+      case ChecklistFilterConditionPB.IsIncomplete:
         return LocaleKeys.grid_checklistFilter_isIncomplted.tr();
       default:
         return "";

+ 12 - 12
frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/condition_list.dart

@@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
 class SelectOptionFilterConditionList extends StatelessWidget {
   final FilterInfo filterInfo;
   final PopoverMutex popoverMutex;
-  final Function(SelectOptionCondition) onCondition;
+  final Function(SelectOptionConditionPB) onCondition;
   const SelectOptionFilterConditionList({
     required this.filterInfo,
     required this.popoverMutex,
@@ -27,7 +27,7 @@ class SelectOptionFilterConditionList extends StatelessWidget {
       asBarrier: true,
       mutex: popoverMutex,
       direction: PopoverDirection.bottomWithCenterAligned,
-      actions: SelectOptionCondition.values
+      actions: SelectOptionConditionPB.values
           .map(
             (action) => ConditionWrapper(
               action,
@@ -59,7 +59,7 @@ class SelectOptionFilterConditionList extends StatelessWidget {
 }
 
 class ConditionWrapper extends ActionCell {
-  final SelectOptionCondition inner;
+  final SelectOptionConditionPB inner;
   final bool isSelected;
   final FieldType fieldType;
 
@@ -84,16 +84,16 @@ class ConditionWrapper extends ActionCell {
   }
 }
 
-extension SelectOptionConditionExtension on SelectOptionCondition {
+extension SelectOptionConditionPBExtension on SelectOptionConditionPB {
   String get singleSelectFilterName {
     switch (this) {
-      case SelectOptionCondition.OptionIs:
+      case SelectOptionConditionPB.OptionIs:
         return LocaleKeys.grid_singleSelectOptionFilter_is.tr();
-      case SelectOptionCondition.OptionIsEmpty:
+      case SelectOptionConditionPB.OptionIsEmpty:
         return LocaleKeys.grid_singleSelectOptionFilter_isEmpty.tr();
-      case SelectOptionCondition.OptionIsNot:
+      case SelectOptionConditionPB.OptionIsNot:
         return LocaleKeys.grid_singleSelectOptionFilter_isNot.tr();
-      case SelectOptionCondition.OptionIsNotEmpty:
+      case SelectOptionConditionPB.OptionIsNotEmpty:
         return LocaleKeys.grid_singleSelectOptionFilter_isNotEmpty.tr();
       default:
         return "";
@@ -102,13 +102,13 @@ extension SelectOptionConditionExtension on SelectOptionCondition {
 
   String get multiSelectFilterName {
     switch (this) {
-      case SelectOptionCondition.OptionIs:
+      case SelectOptionConditionPB.OptionIs:
         return LocaleKeys.grid_multiSelectOptionFilter_contains.tr();
-      case SelectOptionCondition.OptionIsEmpty:
+      case SelectOptionConditionPB.OptionIsEmpty:
         return LocaleKeys.grid_multiSelectOptionFilter_isEmpty.tr();
-      case SelectOptionCondition.OptionIsNot:
+      case SelectOptionConditionPB.OptionIsNot:
         return LocaleKeys.grid_multiSelectOptionFilter_doesNotContain.tr();
-      case SelectOptionCondition.OptionIsNotEmpty:
+      case SelectOptionConditionPB.OptionIsNotEmpty:
         return LocaleKeys.grid_multiSelectOptionFilter_isNotEmpty.tr();
       default:
         return "";

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart

@@ -101,9 +101,9 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
             SliverToBoxAdapter(child: _buildFilterPannel(context, state)),
           ];
 
-          if (state.filter.condition != SelectOptionCondition.OptionIsEmpty &&
+          if (state.filter.condition != SelectOptionConditionPB.OptionIsEmpty &&
               state.filter.condition !=
-                  SelectOptionCondition.OptionIsNotEmpty) {
+                  SelectOptionConditionPB.OptionIsNotEmpty) {
             slivers.add(const SliverToBoxAdapter(child: VSpace(4)));
             slivers.add(
               SliverToBoxAdapter(

+ 25 - 25
frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart

@@ -64,8 +64,8 @@ class _TextFilterChoicechipState extends State<TextFilterChoicechip> {
 
   String _makeFilterDesc(TextFilterEditorState state) {
     String filterDesc = state.filter.condition.choicechipPrefix;
-    if (state.filter.condition == TextFilterCondition.TextIsEmpty ||
-        state.filter.condition == TextFilterCondition.TextIsNotEmpty) {
+    if (state.filter.condition == TextFilterConditionPB.TextIsEmpty ||
+        state.filter.condition == TextFilterConditionPB.TextIsNotEmpty) {
       return filterDesc;
     }
 
@@ -98,8 +98,8 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
             _buildFilterPannel(context, state),
           ];
 
-          if (state.filter.condition != TextFilterCondition.TextIsEmpty &&
-              state.filter.condition != TextFilterCondition.TextIsNotEmpty) {
+          if (state.filter.condition != TextFilterConditionPB.TextIsEmpty &&
+              state.filter.condition != TextFilterConditionPB.TextIsNotEmpty) {
             children.add(const VSpace(4));
             children.add(_buildFilterTextField(context, state));
           }
@@ -120,7 +120,7 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
         children: [
           FlowyText(state.filterInfo.fieldInfo.name),
           const HSpace(4),
-          TextFilterConditionList(
+          TextFilterConditionPBList(
             filterInfo: state.filterInfo,
             popoverMutex: popoverMutex,
             onCondition: (condition) {
@@ -163,11 +163,11 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
   }
 }
 
-class TextFilterConditionList extends StatelessWidget {
+class TextFilterConditionPBList extends StatelessWidget {
   final FilterInfo filterInfo;
   final PopoverMutex popoverMutex;
-  final Function(TextFilterCondition) onCondition;
-  const TextFilterConditionList({
+  final Function(TextFilterConditionPB) onCondition;
+  const TextFilterConditionPBList({
     required this.filterInfo,
     required this.popoverMutex,
     required this.onCondition,
@@ -181,7 +181,7 @@ class TextFilterConditionList extends StatelessWidget {
       asBarrier: true,
       mutex: popoverMutex,
       direction: PopoverDirection.bottomWithCenterAligned,
-      actions: TextFilterCondition.values
+      actions: TextFilterConditionPB.values
           .map(
             (action) => ConditionWrapper(
               action,
@@ -204,7 +204,7 @@ class TextFilterConditionList extends StatelessWidget {
 }
 
 class ConditionWrapper extends ActionCell {
-  final TextFilterCondition inner;
+  final TextFilterConditionPB inner;
   final bool isSelected;
 
   ConditionWrapper(this.inner, this.isSelected);
@@ -222,24 +222,24 @@ class ConditionWrapper extends ActionCell {
   String get name => inner.filterName;
 }
 
-extension TextFilterConditionExtension on TextFilterCondition {
+extension TextFilterConditionPBExtension on TextFilterConditionPB {
   String get filterName {
     switch (this) {
-      case TextFilterCondition.Contains:
+      case TextFilterConditionPB.Contains:
         return LocaleKeys.grid_textFilter_contains.tr();
-      case TextFilterCondition.DoesNotContain:
+      case TextFilterConditionPB.DoesNotContain:
         return LocaleKeys.grid_textFilter_doesNotContain.tr();
-      case TextFilterCondition.EndsWith:
+      case TextFilterConditionPB.EndsWith:
         return LocaleKeys.grid_textFilter_endsWith.tr();
-      case TextFilterCondition.Is:
+      case TextFilterConditionPB.Is:
         return LocaleKeys.grid_textFilter_is.tr();
-      case TextFilterCondition.IsNot:
+      case TextFilterConditionPB.IsNot:
         return LocaleKeys.grid_textFilter_isNot.tr();
-      case TextFilterCondition.StartsWith:
+      case TextFilterConditionPB.StartsWith:
         return LocaleKeys.grid_textFilter_startWith.tr();
-      case TextFilterCondition.TextIsEmpty:
+      case TextFilterConditionPB.TextIsEmpty:
         return LocaleKeys.grid_textFilter_isEmpty.tr();
-      case TextFilterCondition.TextIsNotEmpty:
+      case TextFilterConditionPB.TextIsNotEmpty:
         return LocaleKeys.grid_textFilter_isNotEmpty.tr();
       default:
         return "";
@@ -248,17 +248,17 @@ extension TextFilterConditionExtension on TextFilterCondition {
 
   String get choicechipPrefix {
     switch (this) {
-      case TextFilterCondition.DoesNotContain:
+      case TextFilterConditionPB.DoesNotContain:
         return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
-      case TextFilterCondition.EndsWith:
+      case TextFilterConditionPB.EndsWith:
         return LocaleKeys.grid_textFilter_choicechipPrefix_endWith.tr();
-      case TextFilterCondition.IsNot:
+      case TextFilterConditionPB.IsNot:
         return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
-      case TextFilterCondition.StartsWith:
+      case TextFilterConditionPB.StartsWith:
         return LocaleKeys.grid_textFilter_choicechipPrefix_startWith.tr();
-      case TextFilterCondition.TextIsEmpty:
+      case TextFilterConditionPB.TextIsEmpty:
         return LocaleKeys.grid_textFilter_choicechipPrefix_isEmpty.tr();
-      case TextFilterCondition.TextIsNotEmpty:
+      case TextFilterConditionPB.TextIsNotEmpty:
         return LocaleKeys.grid_textFilter_choicechipPrefix_isNotEmpty.tr();
       default:
         return "";

+ 2 - 11
frontend/app_flowy/test/bloc_test/board_test/util.dart

@@ -1,8 +1,5 @@
-import 'dart:collection';
-
 import 'package:app_flowy/plugins/board/application/board_data_controller.dart';
 import 'package:app_flowy/plugins/board/board.dart';
-import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
@@ -74,10 +71,6 @@ class BoardTestContext {
     return _boardDataController.rowInfos;
   }
 
-  UnmodifiableMapView<String, GridBlockCache> get blocks {
-    return _boardDataController.blocks;
-  }
-
   List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
 
   GridFieldController get fieldController {
@@ -113,15 +106,13 @@ class BoardTestContext {
     String fieldId,
   ) async {
     final RowInfo rowInfo = rowInfos.last;
-    final blockCache = blocks[rowInfo.rowPB.blockId];
-    final rowCache = blockCache?.rowCache;
-
+    final rowCache = _boardDataController.rowCache;
     final fieldController = _boardDataController.fieldController;
 
     final rowDataController = GridRowDataController(
       rowInfo: rowInfo,
       fieldController: fieldController,
-      rowCache: rowCache!,
+      rowCache: rowCache,
     );
 
     final rowBloc = RowBloc(

+ 7 - 7
frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart

@@ -19,7 +19,7 @@ void main() {
     final textField = context.textFieldContext();
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
 
@@ -32,7 +32,7 @@ void main() {
     final textField = context.textFieldContext();
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
 
@@ -60,7 +60,7 @@ void main() {
     final textField = context.textFieldContext();
     service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
 
@@ -81,7 +81,7 @@ void main() {
     final textField = context.textFieldContext();
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
 
@@ -102,7 +102,7 @@ void main() {
     await gridResponseFuture();
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsNotEmpty,
+        condition: TextFilterConditionPB.TextIsNotEmpty,
         content: "");
     await gridResponseFuture();
     assert(context.rowInfos.isEmpty);
@@ -121,7 +121,7 @@ void main() {
     await gridResponseFuture();
     await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
-      condition: CheckboxFilterCondition.IsUnChecked,
+      condition: CheckboxFilterConditionPB.IsUnChecked,
     );
     await gridResponseFuture();
     assert(gridBloc.state.rowInfos.length == 3);
@@ -140,7 +140,7 @@ void main() {
     await gridResponseFuture();
     await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
-      condition: CheckboxFilterCondition.IsChecked,
+      condition: CheckboxFilterConditionPB.IsChecked,
     );
     await gridResponseFuture();
     assert(gridBloc.state.rowInfos.isEmpty);

+ 1 - 1
frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart

@@ -28,7 +28,7 @@ void main() {
     // Insert filter for the text field
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
     assert(menuBloc.state.filters.length == 1);

+ 4 - 4
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart

@@ -23,7 +23,7 @@ void main() {
     final textField = context.textFieldContext();
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
     assert(menuBloc.state.creatableFields.length == 2);
@@ -42,7 +42,7 @@ void main() {
     // Create filter
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
 
@@ -51,11 +51,11 @@ void main() {
     await service.insertTextFilter(
         fieldId: textField.id,
         filterId: textFilter.filter.id,
-        condition: TextFilterCondition.Is,
+        condition: TextFilterConditionPB.Is,
         content: "ABC");
     await gridResponseFuture();
     assert(menuBloc.state.filters.first.textFilter()!.condition ==
-        TextFilterCondition.Is);
+        TextFilterConditionPB.Is);
     assert(menuBloc.state.filters.first.textFilter()!.content == "ABC");
   });
 }

+ 2 - 2
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_by_checkbox_test.dart

@@ -23,7 +23,7 @@ void main() {
     final checkboxField = context.checkboxFieldContext();
     await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
-      condition: CheckboxFilterCondition.IsChecked,
+      condition: CheckboxFilterConditionPB.IsChecked,
     );
     await gridResponseFuture();
     assert(context.rowInfos.length == 1,
@@ -42,7 +42,7 @@ void main() {
     final checkboxField = context.checkboxFieldContext();
     await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
-      condition: CheckboxFilterCondition.IsUnChecked,
+      condition: CheckboxFilterConditionPB.IsUnChecked,
     );
     await gridResponseFuture();
     assert(context.rowInfos.length == 2,

+ 10 - 8
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_by_text_test.dart

@@ -19,7 +19,7 @@ void main() {
     // create a new filter
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
     assert(context.fieldController.filterInfos.length == 1,
@@ -46,7 +46,7 @@ void main() {
     // create a new filter
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsNotEmpty,
+        condition: TextFilterConditionPB.TextIsNotEmpty,
         content: "");
     await gridResponseFuture();
     assert(context.rowInfos.length == 2,
@@ -71,7 +71,7 @@ void main() {
     // create a new filter
     await service.insertTextFilter(
         fieldId: textField.id,
-        condition: TextFilterCondition.TextIsEmpty,
+        condition: TextFilterConditionPB.TextIsEmpty,
         content: "");
     await gridResponseFuture();
     assert(context.fieldController.filterInfos.length == 1,
@@ -84,7 +84,7 @@ void main() {
     await service.insertTextFilter(
         fieldId: textField.id,
         filterId: textFilter.filter.id,
-        condition: TextFilterCondition.TextIsNotEmpty,
+        condition: TextFilterConditionPB.TextIsNotEmpty,
         content: "");
     await gridResponseFuture();
     assert(context.rowInfos.length == 2);
@@ -106,7 +106,9 @@ void main() {
     final textField = context.textFieldContext();
     // create a new filter
     await service.insertTextFilter(
-        fieldId: textField.id, condition: TextFilterCondition.Is, content: "A");
+        fieldId: textField.id,
+        condition: TextFilterConditionPB.Is,
+        content: "A");
     await gridResponseFuture();
     assert(context.rowInfos.length == 1,
         "expect 1 but receive ${context.rowInfos.length}");
@@ -116,7 +118,7 @@ void main() {
     await service.insertTextFilter(
         fieldId: textField.id,
         filterId: textFilter.filter.id,
-        condition: TextFilterCondition.Is,
+        condition: TextFilterConditionPB.Is,
         content: "B");
     await gridResponseFuture();
     assert(context.rowInfos.length == 1);
@@ -125,7 +127,7 @@ void main() {
     await service.insertTextFilter(
         fieldId: textField.id,
         filterId: textFilter.filter.id,
-        condition: TextFilterCondition.Is,
+        condition: TextFilterConditionPB.Is,
         content: "b");
     await gridResponseFuture();
     assert(context.rowInfos.length == 1);
@@ -134,7 +136,7 @@ void main() {
     await service.insertTextFilter(
         fieldId: textField.id,
         filterId: textFilter.filter.id,
-        condition: TextFilterCondition.Is,
+        condition: TextFilterConditionPB.Is,
         content: "C");
     await gridResponseFuture();
     assert(context.rowInfos.isEmpty);

+ 2 - 9
frontend/app_flowy/test/bloc_test/grid_test/util.dart

@@ -1,5 +1,3 @@
-import 'dart:collection';
-import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
@@ -26,10 +24,6 @@ class GridTestContext {
     return gridController.rowInfos;
   }
 
-  UnmodifiableMapView<String, GridBlockCache> get blocks {
-    return gridController.blocks;
-  }
-
   List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
 
   GridFieldController get fieldController {
@@ -71,14 +65,13 @@ class GridTestContext {
     int rowIndex,
   ) async {
     final RowInfo rowInfo = rowInfos[rowIndex];
-    final blockCache = blocks[rowInfo.rowPB.blockId];
-    final rowCache = blockCache?.rowCache;
+    final rowCache = gridController.rowCache;
     final fieldController = gridController.fieldController;
 
     final rowDataController = GridRowDataController(
       rowInfo: rowInfo,
       fieldController: fieldController,
-      rowCache: rowCache!,
+      rowCache: rowCache,
     );
 
     final rowBloc = RowBloc(

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

@@ -6,8 +6,9 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
 pub enum GridDartNotification {
     Unknown = 0,
     DidCreateBlock = 11,
-    DidUpdateGridBlock = 20,
-    DidUpdateGridField = 21,
+    DidUpdateGridViewRows = 20,
+    DidUpdateGridViewRowsVisibility = 21,
+    DidUpdateGridFields = 22,
     DidUpdateRow = 30,
     DidUpdateCell = 40,
     DidUpdateField = 50,
@@ -15,6 +16,7 @@ pub enum GridDartNotification {
     DidUpdateGroup = 61,
     DidGroupByNewField = 62,
     DidUpdateFilter = 63,
+    DidUpdateSort = 64,
     DidUpdateGridSetting = 70,
 }
 

+ 0 - 245
frontend/rust-lib/flowy-grid/src/entities/block_entities.rs

@@ -1,245 +0,0 @@
-use crate::entities::parser::NotEmptyStr;
-use flowy_derive::ProtoBuf;
-use flowy_error::ErrorCode;
-use grid_rev_model::RowRevision;
-use std::sync::Arc;
-
-/// [BlockPB] contains list of row ids. The rows here does not contain any data, just the id
-/// of the row. Check out [RowPB] for more details.
-///
-///
-/// A grid can have many rows. Rows are therefore grouped into Blocks in order to make
-/// things more efficient.
-///                                        |
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct BlockPB {
-    #[pb(index = 1)]
-    pub id: String,
-
-    #[pb(index = 2)]
-    pub rows: Vec<RowPB>,
-}
-
-impl BlockPB {
-    pub fn new(block_id: &str, rows: Vec<RowPB>) -> Self {
-        Self {
-            id: block_id.to_owned(),
-            rows,
-        }
-    }
-}
-
-/// [RowPB] Describes a row. Has the id of the parent Block. Has the metadata of the row.
-#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
-pub struct RowPB {
-    #[pb(index = 1)]
-    pub block_id: String,
-
-    #[pb(index = 2)]
-    pub id: String,
-
-    #[pb(index = 3)]
-    pub height: i32,
-}
-
-impl RowPB {
-    pub fn row_id(&self) -> &str {
-        &self.id
-    }
-
-    pub fn block_id(&self) -> &str {
-        &self.block_id
-    }
-}
-
-impl std::convert::From<&RowRevision> for RowPB {
-    fn from(rev: &RowRevision) -> Self {
-        Self {
-            block_id: rev.block_id.clone(),
-            id: rev.id.clone(),
-            height: rev.height,
-        }
-    }
-}
-
-impl std::convert::From<&mut RowRevision> for RowPB {
-    fn from(rev: &mut RowRevision) -> Self {
-        Self {
-            block_id: rev.block_id.clone(),
-            id: rev.id.clone(),
-            height: rev.height,
-        }
-    }
-}
-
-impl std::convert::From<&Arc<RowRevision>> for RowPB {
-    fn from(rev: &Arc<RowRevision>) -> Self {
-        Self {
-            block_id: rev.block_id.clone(),
-            id: rev.id.clone(),
-            height: rev.height,
-        }
-    }
-}
-
-#[derive(Debug, Default, ProtoBuf)]
-pub struct OptionalRowPB {
-    #[pb(index = 1, one_of)]
-    pub row: Option<RowPB>,
-}
-
-#[derive(Debug, Default, ProtoBuf)]
-pub struct RepeatedRowPB {
-    #[pb(index = 1)]
-    pub items: Vec<RowPB>,
-}
-
-impl std::convert::From<Vec<RowPB>> for RepeatedRowPB {
-    fn from(items: Vec<RowPB>) -> Self {
-        Self { items }
-    }
-}
-
-/// [RepeatedBlockPB] contains list of [BlockPB]
-#[derive(Debug, Default, ProtoBuf)]
-pub struct RepeatedBlockPB {
-    #[pb(index = 1)]
-    pub items: Vec<BlockPB>,
-}
-
-impl std::convert::From<Vec<BlockPB>> for RepeatedBlockPB {
-    fn from(items: Vec<BlockPB>) -> Self {
-        Self { items }
-    }
-}
-
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct InsertedRowPB {
-    #[pb(index = 1)]
-    pub row: RowPB,
-
-    #[pb(index = 2, one_of)]
-    pub index: Option<i32>,
-
-    #[pb(index = 3)]
-    pub is_new: bool,
-}
-
-impl InsertedRowPB {
-    pub fn new(row: RowPB) -> Self {
-        Self {
-            row,
-            index: None,
-            is_new: false,
-        }
-    }
-
-    pub fn with_index(row: RowPB, index: i32) -> Self {
-        Self {
-            row,
-            index: Some(index),
-            is_new: false,
-        }
-    }
-}
-
-impl std::convert::From<RowPB> for InsertedRowPB {
-    fn from(row: RowPB) -> Self {
-        Self {
-            row,
-            index: None,
-            is_new: false,
-        }
-    }
-}
-
-impl std::convert::From<&RowRevision> for InsertedRowPB {
-    fn from(row: &RowRevision) -> Self {
-        let row_order = RowPB::from(row);
-        Self::from(row_order)
-    }
-}
-
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct UpdatedRowPB {
-    #[pb(index = 1)]
-    pub row: RowPB,
-
-    // represents as the cells that were updated in this row.
-    #[pb(index = 2)]
-    pub field_ids: Vec<String>,
-}
-
-#[derive(Debug, Default, Clone, ProtoBuf)]
-pub struct GridBlockChangesetPB {
-    #[pb(index = 1)]
-    pub block_id: String,
-
-    #[pb(index = 2)]
-    pub inserted_rows: Vec<InsertedRowPB>,
-
-    #[pb(index = 3)]
-    pub deleted_rows: Vec<String>,
-
-    #[pb(index = 4)]
-    pub updated_rows: Vec<UpdatedRowPB>,
-
-    #[pb(index = 5)]
-    pub visible_rows: Vec<InsertedRowPB>,
-
-    #[pb(index = 6)]
-    pub invisible_rows: Vec<String>,
-}
-impl GridBlockChangesetPB {
-    pub fn insert(block_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
-        Self {
-            block_id,
-            inserted_rows,
-            ..Default::default()
-        }
-    }
-
-    pub fn delete(block_id: &str, deleted_rows: Vec<String>) -> Self {
-        Self {
-            block_id: block_id.to_owned(),
-            deleted_rows,
-            ..Default::default()
-        }
-    }
-
-    pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowPB>) -> Self {
-        Self {
-            block_id: block_id.to_owned(),
-            updated_rows,
-            ..Default::default()
-        }
-    }
-}
-
-/// [QueryBlocksPayloadPB] is used to query the data of the block that belongs to the grid whose
-/// id is grid_id.
-#[derive(ProtoBuf, Default)]
-pub struct QueryBlocksPayloadPB {
-    #[pb(index = 1)]
-    pub grid_id: String,
-
-    #[pb(index = 2)]
-    pub block_ids: Vec<String>,
-}
-
-pub struct QueryGridBlocksParams {
-    pub grid_id: String,
-    pub block_ids: Vec<String>,
-}
-
-impl TryInto<QueryGridBlocksParams> for QueryBlocksPayloadPB {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<QueryGridBlocksParams, Self::Error> {
-        let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        Ok(QueryGridBlocksParams {
-            grid_id: grid_id.0,
-            block_ids: self.block_ids,
-        })
-    }
-}

+ 11 - 10
frontend/rust-lib/flowy-grid/src/entities/filter_entities/checkbox_filter.rs

@@ -5,35 +5,35 @@ use grid_rev_model::FilterRevision;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct CheckboxFilterPB {
     #[pb(index = 1)]
-    pub condition: CheckboxFilterCondition,
+    pub condition: CheckboxFilterConditionPB,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum CheckboxFilterCondition {
+pub enum CheckboxFilterConditionPB {
     IsChecked = 0,
     IsUnChecked = 1,
 }
 
-impl std::convert::From<CheckboxFilterCondition> for u32 {
-    fn from(value: CheckboxFilterCondition) -> Self {
+impl std::convert::From<CheckboxFilterConditionPB> for u32 {
+    fn from(value: CheckboxFilterConditionPB) -> Self {
         value as u32
     }
 }
 
-impl std::default::Default for CheckboxFilterCondition {
+impl std::default::Default for CheckboxFilterConditionPB {
     fn default() -> Self {
-        CheckboxFilterCondition::IsChecked
+        CheckboxFilterConditionPB::IsChecked
     }
 }
 
-impl std::convert::TryFrom<u8> for CheckboxFilterCondition {
+impl std::convert::TryFrom<u8> for CheckboxFilterConditionPB {
     type Error = ErrorCode;
 
     fn try_from(value: u8) -> Result<Self, Self::Error> {
         match value {
-            0 => Ok(CheckboxFilterCondition::IsChecked),
-            1 => Ok(CheckboxFilterCondition::IsUnChecked),
+            0 => Ok(CheckboxFilterConditionPB::IsChecked),
+            1 => Ok(CheckboxFilterConditionPB::IsUnChecked),
             _ => Err(ErrorCode::InvalidData),
         }
     }
@@ -42,7 +42,8 @@ impl std::convert::TryFrom<u8> for CheckboxFilterCondition {
 impl std::convert::From<&FilterRevision> for CheckboxFilterPB {
     fn from(rev: &FilterRevision) -> Self {
         CheckboxFilterPB {
-            condition: CheckboxFilterCondition::try_from(rev.condition).unwrap_or(CheckboxFilterCondition::IsChecked),
+            condition: CheckboxFilterConditionPB::try_from(rev.condition)
+                .unwrap_or(CheckboxFilterConditionPB::IsChecked),
         }
     }
 }

+ 11 - 11
frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs

@@ -5,35 +5,35 @@ use grid_rev_model::FilterRevision;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct ChecklistFilterPB {
     #[pb(index = 1)]
-    pub condition: ChecklistFilterCondition,
+    pub condition: ChecklistFilterConditionPB,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum ChecklistFilterCondition {
+pub enum ChecklistFilterConditionPB {
     IsComplete = 0,
     IsIncomplete = 1,
 }
 
-impl std::convert::From<ChecklistFilterCondition> for u32 {
-    fn from(value: ChecklistFilterCondition) -> Self {
+impl std::convert::From<ChecklistFilterConditionPB> for u32 {
+    fn from(value: ChecklistFilterConditionPB) -> Self {
         value as u32
     }
 }
 
-impl std::default::Default for ChecklistFilterCondition {
+impl std::default::Default for ChecklistFilterConditionPB {
     fn default() -> Self {
-        ChecklistFilterCondition::IsIncomplete
+        ChecklistFilterConditionPB::IsIncomplete
     }
 }
 
-impl std::convert::TryFrom<u8> for ChecklistFilterCondition {
+impl std::convert::TryFrom<u8> for ChecklistFilterConditionPB {
     type Error = ErrorCode;
 
     fn try_from(value: u8) -> Result<Self, Self::Error> {
         match value {
-            0 => Ok(ChecklistFilterCondition::IsComplete),
-            1 => Ok(ChecklistFilterCondition::IsIncomplete),
+            0 => Ok(ChecklistFilterConditionPB::IsComplete),
+            1 => Ok(ChecklistFilterConditionPB::IsIncomplete),
             _ => Err(ErrorCode::InvalidData),
         }
     }
@@ -42,8 +42,8 @@ impl std::convert::TryFrom<u8> for ChecklistFilterCondition {
 impl std::convert::From<&FilterRevision> for ChecklistFilterPB {
     fn from(rev: &FilterRevision) -> Self {
         ChecklistFilterPB {
-            condition: ChecklistFilterCondition::try_from(rev.condition)
-                .unwrap_or(ChecklistFilterCondition::IsIncomplete),
+            condition: ChecklistFilterConditionPB::try_from(rev.condition)
+                .unwrap_or(ChecklistFilterConditionPB::IsIncomplete),
         }
     }
 }

+ 19 - 19
frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs

@@ -7,7 +7,7 @@ use std::str::FromStr;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct DateFilterPB {
     #[pb(index = 1)]
-    pub condition: DateFilterCondition,
+    pub condition: DateFilterConditionPB,
 
     #[pb(index = 2, one_of)]
     pub start: Option<i64>,
@@ -20,19 +20,19 @@ pub struct DateFilterPB {
 }
 
 #[derive(Deserialize, Serialize, Default, Clone, Debug)]
-pub struct DateFilterContent {
+pub struct DateFilterContentPB {
     pub start: Option<i64>,
     pub end: Option<i64>,
     pub timestamp: Option<i64>,
 }
 
-impl ToString for DateFilterContent {
+impl ToString for DateFilterContentPB {
     fn to_string(&self) -> String {
         serde_json::to_string(self).unwrap()
     }
 }
 
-impl FromStr for DateFilterContent {
+impl FromStr for DateFilterContentPB {
     type Err = serde_json::Error;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -42,7 +42,7 @@ impl FromStr for DateFilterContent {
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum DateFilterCondition {
+pub enum DateFilterConditionPB {
     DateIs = 0,
     DateBefore = 1,
     DateAfter = 2,
@@ -53,42 +53,42 @@ pub enum DateFilterCondition {
     DateIsNotEmpty = 7,
 }
 
-impl std::convert::From<DateFilterCondition> for u32 {
-    fn from(value: DateFilterCondition) -> Self {
+impl std::convert::From<DateFilterConditionPB> for u32 {
+    fn from(value: DateFilterConditionPB) -> Self {
         value as u32
     }
 }
-impl std::default::Default for DateFilterCondition {
+impl std::default::Default for DateFilterConditionPB {
     fn default() -> Self {
-        DateFilterCondition::DateIs
+        DateFilterConditionPB::DateIs
     }
 }
 
-impl std::convert::TryFrom<u8> for DateFilterCondition {
+impl std::convert::TryFrom<u8> for DateFilterConditionPB {
     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),
+            0 => Ok(DateFilterConditionPB::DateIs),
+            1 => Ok(DateFilterConditionPB::DateBefore),
+            2 => Ok(DateFilterConditionPB::DateAfter),
+            3 => Ok(DateFilterConditionPB::DateOnOrBefore),
+            4 => Ok(DateFilterConditionPB::DateOnOrAfter),
+            5 => Ok(DateFilterConditionPB::DateWithIn),
+            6 => Ok(DateFilterConditionPB::DateIsEmpty),
             _ => Err(ErrorCode::InvalidData),
         }
     }
 }
 impl std::convert::From<&FilterRevision> for DateFilterPB {
     fn from(rev: &FilterRevision) -> Self {
-        let condition = DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs);
+        let condition = DateFilterConditionPB::try_from(rev.condition).unwrap_or(DateFilterConditionPB::DateIs);
         let mut filter = DateFilterPB {
             condition,
             ..Default::default()
         };
 
-        if let Ok(content) = DateFilterContent::from_str(&rev.content) {
+        if let Ok(content) = DateFilterContentPB::from_str(&rev.content) {
             filter.start = content.start;
             filter.end = content.end;
             filter.timestamp = content.timestamp;

+ 16 - 16
frontend/rust-lib/flowy-grid/src/entities/filter_entities/number_filter.rs

@@ -5,7 +5,7 @@ use grid_rev_model::FilterRevision;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct NumberFilterPB {
     #[pb(index = 1)]
-    pub condition: NumberFilterCondition,
+    pub condition: NumberFilterConditionPB,
 
     #[pb(index = 2)]
     pub content: String,
@@ -13,7 +13,7 @@ pub struct NumberFilterPB {
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum NumberFilterCondition {
+pub enum NumberFilterConditionPB {
     Equal = 0,
     NotEqual = 1,
     GreaterThan = 2,
@@ -24,30 +24,30 @@ pub enum NumberFilterCondition {
     NumberIsNotEmpty = 7,
 }
 
-impl std::default::Default for NumberFilterCondition {
+impl std::default::Default for NumberFilterConditionPB {
     fn default() -> Self {
-        NumberFilterCondition::Equal
+        NumberFilterConditionPB::Equal
     }
 }
 
-impl std::convert::From<NumberFilterCondition> for u32 {
-    fn from(value: NumberFilterCondition) -> Self {
+impl std::convert::From<NumberFilterConditionPB> for u32 {
+    fn from(value: NumberFilterConditionPB) -> Self {
         value as u32
     }
 }
-impl std::convert::TryFrom<u8> for NumberFilterCondition {
+impl std::convert::TryFrom<u8> for NumberFilterConditionPB {
     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),
+            0 => Ok(NumberFilterConditionPB::Equal),
+            1 => Ok(NumberFilterConditionPB::NotEqual),
+            2 => Ok(NumberFilterConditionPB::GreaterThan),
+            3 => Ok(NumberFilterConditionPB::LessThan),
+            4 => Ok(NumberFilterConditionPB::GreaterThanOrEqualTo),
+            5 => Ok(NumberFilterConditionPB::LessThanOrEqualTo),
+            6 => Ok(NumberFilterConditionPB::NumberIsEmpty),
+            7 => Ok(NumberFilterConditionPB::NumberIsNotEmpty),
             _ => Err(ErrorCode::InvalidData),
         }
     }
@@ -56,7 +56,7 @@ impl std::convert::TryFrom<u8> for NumberFilterCondition {
 impl std::convert::From<&FilterRevision> for NumberFilterPB {
     fn from(rev: &FilterRevision) -> Self {
         NumberFilterPB {
-            condition: NumberFilterCondition::try_from(rev.condition).unwrap_or(NumberFilterCondition::Equal),
+            condition: NumberFilterConditionPB::try_from(rev.condition).unwrap_or(NumberFilterConditionPB::Equal),
             content: rev.content.clone(),
         }
     }

+ 12 - 12
frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs

@@ -6,7 +6,7 @@ use grid_rev_model::FilterRevision;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct SelectOptionFilterPB {
     #[pb(index = 1)]
-    pub condition: SelectOptionCondition,
+    pub condition: SelectOptionConditionPB,
 
     #[pb(index = 2)]
     pub option_ids: Vec<String>,
@@ -14,34 +14,34 @@ pub struct SelectOptionFilterPB {
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum SelectOptionCondition {
+pub enum SelectOptionConditionPB {
     OptionIs = 0,
     OptionIsNot = 1,
     OptionIsEmpty = 2,
     OptionIsNotEmpty = 3,
 }
 
-impl std::convert::From<SelectOptionCondition> for u32 {
-    fn from(value: SelectOptionCondition) -> Self {
+impl std::convert::From<SelectOptionConditionPB> for u32 {
+    fn from(value: SelectOptionConditionPB) -> Self {
         value as u32
     }
 }
 
-impl std::default::Default for SelectOptionCondition {
+impl std::default::Default for SelectOptionConditionPB {
     fn default() -> Self {
-        SelectOptionCondition::OptionIs
+        SelectOptionConditionPB::OptionIs
     }
 }
 
-impl std::convert::TryFrom<u8> for SelectOptionCondition {
+impl std::convert::TryFrom<u8> for SelectOptionConditionPB {
     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),
+            0 => Ok(SelectOptionConditionPB::OptionIs),
+            1 => Ok(SelectOptionConditionPB::OptionIsNot),
+            2 => Ok(SelectOptionConditionPB::OptionIsEmpty),
+            3 => Ok(SelectOptionConditionPB::OptionIsNotEmpty),
             _ => Err(ErrorCode::InvalidData),
         }
     }
@@ -51,7 +51,7 @@ impl std::convert::From<&FilterRevision> for SelectOptionFilterPB {
     fn from(rev: &FilterRevision) -> Self {
         let ids = SelectOptionIds::from(rev.content.clone());
         SelectOptionFilterPB {
-            condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs),
+            condition: SelectOptionConditionPB::try_from(rev.condition).unwrap_or(SelectOptionConditionPB::OptionIs),
             option_ids: ids.into_inner(),
         }
     }

+ 16 - 16
frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs

@@ -5,7 +5,7 @@ use grid_rev_model::FilterRevision;
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct TextFilterPB {
     #[pb(index = 1)]
-    pub condition: TextFilterCondition,
+    pub condition: TextFilterConditionPB,
 
     #[pb(index = 2)]
     pub content: String,
@@ -13,7 +13,7 @@ pub struct TextFilterPB {
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
-pub enum TextFilterCondition {
+pub enum TextFilterConditionPB {
     Is = 0,
     IsNot = 1,
     Contains = 2,
@@ -24,31 +24,31 @@ pub enum TextFilterCondition {
     TextIsNotEmpty = 7,
 }
 
-impl std::convert::From<TextFilterCondition> for u32 {
-    fn from(value: TextFilterCondition) -> Self {
+impl std::convert::From<TextFilterConditionPB> for u32 {
+    fn from(value: TextFilterConditionPB) -> Self {
         value as u32
     }
 }
 
-impl std::default::Default for TextFilterCondition {
+impl std::default::Default for TextFilterConditionPB {
     fn default() -> Self {
-        TextFilterCondition::Is
+        TextFilterConditionPB::Is
     }
 }
 
-impl std::convert::TryFrom<u8> for TextFilterCondition {
+impl std::convert::TryFrom<u8> for TextFilterConditionPB {
     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),
+            0 => Ok(TextFilterConditionPB::Is),
+            1 => Ok(TextFilterConditionPB::IsNot),
+            2 => Ok(TextFilterConditionPB::Contains),
+            3 => Ok(TextFilterConditionPB::DoesNotContain),
+            4 => Ok(TextFilterConditionPB::StartsWith),
+            5 => Ok(TextFilterConditionPB::EndsWith),
+            6 => Ok(TextFilterConditionPB::TextIsEmpty),
+            7 => Ok(TextFilterConditionPB::TextIsNotEmpty),
             _ => Err(ErrorCode::InvalidData),
         }
     }
@@ -57,7 +57,7 @@ impl std::convert::TryFrom<u8> for TextFilterCondition {
 impl std::convert::From<&FilterRevision> for TextFilterPB {
     fn from(rev: &FilterRevision) -> Self {
         TextFilterPB {
-            condition: TextFilterCondition::try_from(rev.condition).unwrap_or(TextFilterCondition::Is),
+            condition: TextFilterConditionPB::try_from(rev.condition).unwrap_or(TextFilterConditionPB::Is),
             content: rev.content.clone(),
         }
     }

+ 31 - 4
frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs

@@ -1,6 +1,6 @@
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{
-    CheckboxFilterPB, ChecklistFilterPB, DateFilterContent, DateFilterPB, FieldType, NumberFilterPB,
+    CheckboxFilterPB, ChecklistFilterPB, DateFilterContentPB, DateFilterPB, FieldType, NumberFilterPB,
     SelectOptionFilterPB, TextFilterPB,
 };
 use crate::services::field::SelectOptionIds;
@@ -79,12 +79,18 @@ pub struct DeleteFilterPayloadPB {
 
     #[pb(index = 3)]
     pub filter_id: String,
+
+    #[pb(index = 4)]
+    pub view_id: String,
 }
 
 impl TryInto<DeleteFilterParams> for DeleteFilterPayloadPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<DeleteFilterParams, Self::Error> {
+        let view_id = NotEmptyStr::parse(self.view_id)
+            .map_err(|_| ErrorCode::GridViewIdIsEmpty)?
+            .0;
         let field_id = NotEmptyStr::parse(self.field_id)
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
@@ -98,12 +104,17 @@ impl TryInto<DeleteFilterParams> for DeleteFilterPayloadPB {
             field_type: self.field_type,
         };
 
-        Ok(DeleteFilterParams { filter_id, filter_type })
+        Ok(DeleteFilterParams {
+            view_id,
+            filter_id,
+            filter_type,
+        })
     }
 }
 
 #[derive(Debug)]
 pub struct DeleteFilterParams {
+    pub view_id: String,
     pub filter_type: FilterType,
     pub filter_id: String,
 }
@@ -116,18 +127,27 @@ pub struct AlterFilterPayloadPB {
     #[pb(index = 2)]
     pub field_type: FieldType,
 
+    /// Create a new filter if the filter_id is None
     #[pb(index = 3, one_of)]
     pub filter_id: Option<String>,
 
     #[pb(index = 4)]
     pub data: Vec<u8>,
+
+    #[pb(index = 5)]
+    pub view_id: String,
 }
 
 impl AlterFilterPayloadPB {
     #[allow(dead_code)]
-    pub fn new<T: TryInto<Bytes, Error = ::protobuf::ProtobufError>>(field_rev: &FieldRevision, data: T) -> Self {
+    pub fn new<T: TryInto<Bytes, Error = ::protobuf::ProtobufError>>(
+        view_id: &str,
+        field_rev: &FieldRevision,
+        data: T,
+    ) -> Self {
         let data = data.try_into().unwrap_or_else(|_| Bytes::new());
         Self {
+            view_id: view_id.to_owned(),
             field_id: field_rev.id.clone(),
             field_type: field_rev.ty.into(),
             filter_id: None,
@@ -140,6 +160,10 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<AlterFilterParams, Self::Error> {
+        let view_id = NotEmptyStr::parse(self.view_id)
+            .map_err(|_| ErrorCode::GridViewIdIsEmpty)?
+            .0;
+
         let field_id = NotEmptyStr::parse(self.field_id)
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
@@ -169,7 +193,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
             FieldType::DateTime => {
                 let filter = DateFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?;
                 condition = filter.condition as u8;
-                content = DateFilterContent {
+                content = DateFilterContentPB {
                     start: filter.start,
                     end: filter.end,
                     timestamp: filter.timestamp,
@@ -184,6 +208,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
         }
 
         Ok(AlterFilterParams {
+            view_id,
             field_id,
             filter_id,
             field_type: self.field_type.into(),
@@ -195,7 +220,9 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
 
 #[derive(Debug)]
 pub struct AlterFilterParams {
+    pub view_id: String,
     pub field_id: String,
+    /// Create a new filter if the filter_id is None
     pub filter_id: Option<String>,
     pub field_type: FieldTypeRevision,
     pub condition: u8,

+ 2 - 2
frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs

@@ -1,5 +1,5 @@
 use crate::entities::parser::NotEmptyStr;
-use crate::entities::{BlockPB, FieldIdPB};
+use crate::entities::{FieldIdPB, RowPB};
 use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
 
@@ -13,7 +13,7 @@ pub struct GridPB {
     pub fields: Vec<FieldIdPB>,
 
     #[pb(index = 3)]
-    pub blocks: Vec<BlockPB>,
+    pub rows: Vec<RowPB>,
 }
 
 #[derive(ProtoBuf, Default)]

+ 4 - 2
frontend/rust-lib/flowy-grid/src/entities/mod.rs

@@ -1,4 +1,3 @@
-pub mod block_entities;
 mod cell_entities;
 mod field_entities;
 pub mod filter_entities;
@@ -7,8 +6,9 @@ mod group_entities;
 pub mod parser;
 mod row_entities;
 pub mod setting_entities;
+mod sort_entities;
+mod view_entities;
 
-pub use block_entities::*;
 pub use cell_entities::*;
 pub use field_entities::*;
 pub use filter_entities::*;
@@ -16,3 +16,5 @@ pub use grid_entities::*;
 pub use group_entities::*;
 pub use row_entities::*;
 pub use setting_entities::*;
+pub use sort_entities::*;
+pub use view_entities::*;

+ 130 - 6
frontend/rust-lib/flowy-grid/src/entities/row_entities.rs

@@ -2,6 +2,136 @@ use crate::entities::parser::NotEmptyStr;
 use crate::entities::GridLayout;
 use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
+use grid_rev_model::RowRevision;
+use std::sync::Arc;
+
+/// [RowPB] Describes a row. Has the id of the parent Block. Has the metadata of the row.
+#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
+pub struct RowPB {
+    #[pb(index = 1)]
+    pub block_id: String,
+
+    #[pb(index = 2)]
+    pub id: String,
+
+    #[pb(index = 3)]
+    pub height: i32,
+}
+
+impl RowPB {
+    pub fn row_id(&self) -> &str {
+        &self.id
+    }
+
+    pub fn block_id(&self) -> &str {
+        &self.block_id
+    }
+}
+
+impl std::convert::From<&RowRevision> for RowPB {
+    fn from(rev: &RowRevision) -> Self {
+        Self {
+            block_id: rev.block_id.clone(),
+            id: rev.id.clone(),
+            height: rev.height,
+        }
+    }
+}
+
+impl std::convert::From<&mut RowRevision> for RowPB {
+    fn from(rev: &mut RowRevision) -> Self {
+        Self {
+            block_id: rev.block_id.clone(),
+            id: rev.id.clone(),
+            height: rev.height,
+        }
+    }
+}
+
+impl std::convert::From<&Arc<RowRevision>> for RowPB {
+    fn from(rev: &Arc<RowRevision>) -> Self {
+        Self {
+            block_id: rev.block_id.clone(),
+            id: rev.id.clone(),
+            height: rev.height,
+        }
+    }
+}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct OptionalRowPB {
+    #[pb(index = 1, one_of)]
+    pub row: Option<RowPB>,
+}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct RepeatedRowPB {
+    #[pb(index = 1)]
+    pub items: Vec<RowPB>,
+}
+
+impl std::convert::From<Vec<RowPB>> for RepeatedRowPB {
+    fn from(items: Vec<RowPB>) -> Self {
+        Self { items }
+    }
+}
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct InsertedRowPB {
+    #[pb(index = 1)]
+    pub row: RowPB,
+
+    #[pb(index = 2, one_of)]
+    pub index: Option<i32>,
+
+    #[pb(index = 3)]
+    pub is_new: bool,
+}
+
+impl InsertedRowPB {
+    pub fn new(row: RowPB) -> Self {
+        Self {
+            row,
+            index: None,
+            is_new: false,
+        }
+    }
+
+    pub fn with_index(row: RowPB, index: i32) -> Self {
+        Self {
+            row,
+            index: Some(index),
+            is_new: false,
+        }
+    }
+}
+
+impl std::convert::From<RowPB> for InsertedRowPB {
+    fn from(row: RowPB) -> Self {
+        Self {
+            row,
+            index: None,
+            is_new: false,
+        }
+    }
+}
+
+impl std::convert::From<&RowRevision> for InsertedRowPB {
+    fn from(row: &RowRevision) -> Self {
+        let row_order = RowPB::from(row);
+        Self::from(row_order)
+    }
+}
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct UpdatedRowPB {
+    #[pb(index = 1)]
+    pub row: RowPB,
+
+    // represents as the cells that were updated in this row.
+    #[pb(index = 2)]
+    pub field_ids: Vec<String>,
+}
 
 #[derive(Debug, Default, Clone, ProtoBuf)]
 pub struct RowIdPB {
@@ -9,15 +139,11 @@ pub struct RowIdPB {
     pub grid_id: String,
 
     #[pb(index = 2)]
-    pub block_id: String,
-
-    #[pb(index = 3)]
     pub row_id: String,
 }
 
 pub struct RowIdParams {
     pub grid_id: String,
-    pub block_id: String,
     pub row_id: String,
 }
 
@@ -26,12 +152,10 @@ impl TryInto<RowIdParams> for RowIdPB {
 
     fn try_into(self) -> Result<RowIdParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        let block_id = NotEmptyStr::parse(self.block_id).map_err(|_| ErrorCode::BlockIdIsEmpty)?;
         let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
 
         Ok(RowIdParams {
             grid_id: grid_id.0,
-            block_id: block_id.0,
             row_id: row_id.0,
         })
     }

+ 25 - 4
frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs

@@ -1,7 +1,8 @@
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{
-    AlterFilterParams, AlterFilterPayloadPB, DeleteFilterParams, DeleteFilterPayloadPB, DeleteGroupParams,
-    DeleteGroupPayloadPB, InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB, RepeatedGroupConfigurationPB,
+    AlterFilterParams, AlterFilterPayloadPB, AlterSortParams, AlterSortPayloadPB, DeleteFilterParams,
+    DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, DeleteSortParams, DeleteSortPayloadPB,
+    InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB, RepeatedGroupConfigurationPB,
 };
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
@@ -83,7 +84,7 @@ pub struct GridSettingChangesetPB {
     pub layout_type: GridLayout,
 
     #[pb(index = 3, one_of)]
-    pub insert_filter: Option<AlterFilterPayloadPB>,
+    pub alter_filter: Option<AlterFilterPayloadPB>,
 
     #[pb(index = 4, one_of)]
     pub delete_filter: Option<DeleteFilterPayloadPB>,
@@ -93,6 +94,12 @@ pub struct GridSettingChangesetPB {
 
     #[pb(index = 6, one_of)]
     pub delete_group: Option<DeleteGroupPayloadPB>,
+
+    #[pb(index = 7, one_of)]
+    pub alter_sort: Option<AlterSortPayloadPB>,
+
+    #[pb(index = 8, one_of)]
+    pub delete_sort: Option<DeleteSortPayloadPB>,
 }
 
 impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
@@ -103,7 +110,7 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
             .map_err(|_| ErrorCode::ViewIdInvalid)?
             .0;
 
-        let insert_filter = match self.insert_filter {
+        let insert_filter = match self.alter_filter {
             None => None,
             Some(payload) => Some(payload.try_into()?),
         };
@@ -123,6 +130,16 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
             None => None,
         };
 
+        let alert_sort = match self.alter_sort {
+            None => None,
+            Some(payload) => Some(payload.try_into()?),
+        };
+
+        let delete_sort = match self.delete_sort {
+            None => None,
+            Some(payload) => Some(payload.try_into()?),
+        };
+
         Ok(GridSettingChangesetParams {
             grid_id: view_id,
             layout_type: self.layout_type.into(),
@@ -130,6 +147,8 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
             delete_filter,
             insert_group,
             delete_group,
+            alert_sort,
+            delete_sort,
         })
     }
 }
@@ -141,6 +160,8 @@ pub struct GridSettingChangesetParams {
     pub delete_filter: Option<DeleteFilterParams>,
     pub insert_group: Option<InsertGroupParams>,
     pub delete_group: Option<DeleteGroupParams>,
+    pub alert_sort: Option<AlterSortParams>,
+    pub delete_sort: Option<DeleteSortParams>,
 }
 
 impl GridSettingChangesetParams {

+ 153 - 0
frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs

@@ -0,0 +1,153 @@
+use crate::entities::parser::NotEmptyStr;
+use crate::entities::FieldType;
+use crate::services::sort::SortType;
+
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+use flowy_error::ErrorCode;
+use grid_rev_model::FieldTypeRevision;
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridSortPB {
+    #[pb(index = 1)]
+    pub id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub field_type: FieldType,
+
+    #[pb(index = 4)]
+    pub condition: GridSortConditionPB,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum GridSortConditionPB {
+    Ascending = 0,
+    Descending = 1,
+}
+impl std::default::Default for GridSortConditionPB {
+    fn default() -> Self {
+        Self::Ascending
+    }
+}
+#[derive(ProtoBuf, Debug, Default, Clone)]
+pub struct AlterSortPayloadPB {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub field_type: FieldType,
+
+    /// Create a new filter if the filter_id is None
+    #[pb(index = 4, one_of)]
+    pub sort_id: Option<String>,
+
+    #[pb(index = 5)]
+    pub condition: GridSortConditionPB,
+}
+
+impl TryInto<AlterSortParams> for AlterSortPayloadPB {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<AlterSortParams, Self::Error> {
+        let view_id = NotEmptyStr::parse(self.view_id)
+            .map_err(|_| ErrorCode::GridViewIdIsEmpty)?
+            .0;
+
+        let field_id = NotEmptyStr::parse(self.field_id)
+            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .0;
+        let sort_id = match self.sort_id {
+            None => None,
+            Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FilterIdIsEmpty)?.0),
+        };
+
+        Ok(AlterSortParams {
+            view_id,
+            field_id,
+            sort_id,
+            field_type: self.field_type.into(),
+            condition: self.condition as u8,
+        })
+    }
+}
+
+#[derive(Debug)]
+pub struct AlterSortParams {
+    pub view_id: String,
+    pub field_id: String,
+    /// Create a new sort if the sort is None
+    pub sort_id: Option<String>,
+    pub field_type: FieldTypeRevision,
+    pub condition: u8,
+}
+
+#[derive(ProtoBuf, Debug, Default, Clone)]
+pub struct DeleteSortPayloadPB {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub field_type: FieldType,
+
+    #[pb(index = 4)]
+    pub sort_id: String,
+}
+
+impl TryInto<DeleteSortParams> for DeleteSortPayloadPB {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<DeleteSortParams, Self::Error> {
+        let view_id = NotEmptyStr::parse(self.view_id)
+            .map_err(|_| ErrorCode::GridViewIdIsEmpty)?
+            .0;
+        let field_id = NotEmptyStr::parse(self.field_id)
+            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .0;
+
+        let sort_id = NotEmptyStr::parse(self.sort_id)
+            .map_err(|_| ErrorCode::UnexpectedEmptyString)?
+            .0;
+
+        let sort_type = SortType {
+            field_id,
+            field_type: self.field_type,
+        };
+
+        Ok(DeleteSortParams {
+            view_id,
+            sort_type,
+            sort_id,
+        })
+    }
+}
+
+#[derive(Debug)]
+pub struct DeleteSortParams {
+    pub view_id: String,
+    pub sort_type: SortType,
+    pub sort_id: String,
+}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct SortChangesetNotificationPB {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 2)]
+    pub insert_sorts: Vec<GridSortPB>,
+
+    #[pb(index = 3)]
+    pub delete_sorts: Vec<GridSortPB>,
+
+    #[pb(index = 4)]
+    pub update_sorts: Vec<GridSortPB>,
+}

+ 64 - 0
frontend/rust-lib/flowy-grid/src/entities/view_entities.rs

@@ -0,0 +1,64 @@
+use crate::entities::{InsertedRowPB, UpdatedRowPB};
+use flowy_derive::ProtoBuf;
+
+#[derive(Debug, Default, Clone, ProtoBuf)]
+pub struct GridRowsVisibilityChangesetPB {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 5)]
+    pub visible_rows: Vec<InsertedRowPB>,
+
+    #[pb(index = 6)]
+    pub invisible_rows: Vec<String>,
+}
+
+#[derive(Debug, Default, Clone, ProtoBuf)]
+pub struct GridViewRowsChangesetPB {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 2)]
+    pub inserted_rows: Vec<InsertedRowPB>,
+
+    #[pb(index = 3)]
+    pub deleted_rows: Vec<String>,
+
+    #[pb(index = 4)]
+    pub updated_rows: Vec<UpdatedRowPB>,
+}
+
+impl GridViewRowsChangesetPB {
+    pub fn from_insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
+        Self {
+            view_id,
+            inserted_rows,
+            ..Default::default()
+        }
+    }
+
+    pub fn from_delete(view_id: String, deleted_rows: Vec<String>) -> Self {
+        Self {
+            view_id,
+            deleted_rows,
+            ..Default::default()
+        }
+    }
+
+    pub fn from_update(view_id: String, updated_rows: Vec<UpdatedRowPB>) -> Self {
+        Self {
+            view_id,
+            updated_rows,
+            ..Default::default()
+        }
+    }
+
+    pub fn from_move(view_id: String, deleted_rows: Vec<String>, inserted_rows: Vec<InsertedRowPB>) -> Self {
+        Self {
+            view_id,
+            inserted_rows,
+            deleted_rows,
+            ..Default::default()
+        }
+    }
+}

+ 12 - 16
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -7,7 +7,7 @@ use crate::services::field::{
     SelectOptionCellChangesetParams, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPB,
     SelectOptionPB,
 };
-use crate::services::row::{make_block_pbs, make_row_from_row_rev};
+use crate::services::row::make_row_from_row_rev;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use grid_rev_model::FieldRevision;
 use lib_dispatch::prelude::{data_result, AFPluginData, AFPluginState, DataResult};
@@ -19,8 +19,8 @@ pub(crate) async fn get_grid_handler(
     manager: AFPluginState<Arc<GridManager>>,
 ) -> DataResult<GridPB, FlowyError> {
     let grid_id: GridIdPB = data.into_inner();
-    let editor = manager.open_grid(grid_id).await?;
-    let grid = editor.get_grid().await?;
+    let editor = manager.open_grid(grid_id.as_ref()).await?;
+    let grid = editor.get_grid(grid_id.as_ref()).await?;
     data_result(grid)
 }
 
@@ -58,6 +58,13 @@ pub(crate) async fn update_grid_setting_handler(
     if let Some(delete_filter) = params.delete_filter {
         let _ = editor.delete_filter(delete_filter).await?;
     }
+
+    if let Some(alter_sort) = params.alert_sort {
+        let _ = editor.create_or_update_sort(alter_sort).await?;
+    }
+    if let Some(delete_sort) = params.delete_sort {
+        let _ = editor.delete_sort(delete_sort).await?;
+    }
     Ok(())
 }
 
@@ -74,17 +81,6 @@ pub(crate) async fn get_all_filters_handler(
     data_result(filters)
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
-pub(crate) async fn get_grid_blocks_handler(
-    data: AFPluginData<QueryBlocksPayloadPB>,
-    manager: AFPluginState<Arc<GridManager>>,
-) -> DataResult<RepeatedBlockPB, FlowyError> {
-    let params: QueryGridBlocksParams = data.into_inner().try_into()?;
-    let editor = manager.get_grid_editor(&params.grid_id).await?;
-    let blocks = editor.get_blocks(Some(params.block_ids)).await?;
-    data_result(make_block_pbs(blocks))
-}
-
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_fields_handler(
     data: AFPluginData<GetFieldPayloadPB>,
@@ -415,14 +411,14 @@ pub(crate) async fn get_select_option_handler(
             //
             let cell_rev = editor.get_cell_rev(&params.row_id, &params.field_id).await?;
             let type_option = select_type_option_from_field_rev(&field_rev)?;
-            let any_cell_data: TypeCellData = match cell_rev {
+            let type_cell_data: TypeCellData = match cell_rev {
                 None => TypeCellData {
                     data: "".to_string(),
                     field_type: field_rev.ty.into(),
                 },
                 Some(cell_rev) => cell_rev.try_into()?,
             };
-            let selected_options = type_option.get_selected_options(any_cell_data.into());
+            let selected_options = type_option.get_selected_options(type_cell_data.into());
             data_result(selected_options)
         }
     }

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

@@ -9,7 +9,7 @@ pub fn init(grid_manager: Arc<GridManager>) -> AFPlugin {
     let mut plugin = AFPlugin::new().name(env!("CARGO_PKG_NAME")).state(grid_manager);
     plugin = plugin
         .event(GridEvent::GetGrid, get_grid_handler)
-        .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
+        // .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
         .event(GridEvent::GetGridSetting, get_grid_setting_handler)
         .event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
         .event(GridEvent::GetAllFilters, get_all_filters_handler)
@@ -59,13 +59,6 @@ pub enum GridEvent {
     #[event(input = "GridIdPB", output = "GridPB")]
     GetGrid = 0,
 
-    /// [GetGridBlocks] event is used to get the grid's block.
-    ///
-    /// The event handler accepts a [QueryBlocksPayloadPB] and returns a [RepeatedBlockPB]
-    /// if there are no errors.
-    #[event(input = "QueryBlocksPayloadPB", output = "RepeatedBlockPB")]
-    GetGridBlocks = 1,
-
     /// [GetGridSetting] event is used to get the grid's settings.
     ///
     /// The event handler accepts [GridIdPB] and return [GridSettingPB]

+ 4 - 21
frontend/rust-lib/flowy-grid/src/services/block_editor.rs

@@ -1,4 +1,3 @@
-use crate::entities::RowPB;
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_http_model::revision::Revision;
@@ -114,6 +113,10 @@ impl GridBlockRevisionEditor {
         self.pad.read().await.index_of_row(row_id)
     }
 
+    pub async fn number_of_rows(&self) -> i32 {
+        self.pad.read().await.rows.len() as i32
+    }
+
     pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<(usize, Arc<RowRevision>)>> {
         let row_rev = self.pad.read().await.get_row_rev(row_id);
         Ok(row_rev)
@@ -136,26 +139,6 @@ impl GridBlockRevisionEditor {
         Ok(cell_revs)
     }
 
-    pub async fn get_row_pb(&self, row_id: &str) -> FlowyResult<Option<RowPB>> {
-        let row_ids = Some(vec![Cow::Borrowed(row_id)]);
-        Ok(self.get_row_pbs(row_ids).await?.pop())
-    }
-
-    pub async fn get_row_pbs<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<RowPB>>
-    where
-        T: AsRef<str> + ToOwned + ?Sized,
-    {
-        let row_infos = self
-            .pad
-            .read()
-            .await
-            .get_row_revs(row_ids)?
-            .iter()
-            .map(RowPB::from)
-            .collect::<Vec<RowPB>>();
-        Ok(row_infos)
-    }
-
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
         F: for<'a> FnOnce(&'a mut GridBlockRevisionPad) -> FlowyResult<Option<GridBlockRevisionChangeset>>,

+ 71 - 62
frontend/rust-lib/flowy-grid/src/services/block_manager.rs

@@ -1,12 +1,12 @@
 use crate::dart_notification::{send_dart_notification, GridDartNotification};
-use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB, UpdatedRowPB};
+use crate::entities::{CellChangesetPB, InsertedRowPB, UpdatedRowPB};
 use crate::manager::GridUser;
 use crate::services::block_editor::{GridBlockRevisionEditor, GridBlockRevisionMergeable};
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::persistence::rev_sqlite::{
     SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionSnapshotPersistence,
 };
-use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlock};
+use crate::services::row::{make_row_from_row_rev, GridBlockRow, GridBlockRowRevision};
 use dashmap::DashMap;
 use flowy_database::ConnectionPool;
 use flowy_error::FlowyResult;
@@ -15,12 +15,35 @@ use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowC
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::sync::Arc;
+use tokio::sync::broadcast;
+
+#[derive(Debug, Clone)]
+pub enum GridBlockEvent {
+    InsertRow {
+        block_id: String,
+        row: InsertedRowPB,
+    },
+    UpdateRow {
+        block_id: String,
+        row: UpdatedRowPB,
+    },
+    DeleteRow {
+        block_id: String,
+        row_id: String,
+    },
+    Move {
+        block_id: String,
+        deleted_row_id: String,
+        inserted_row: InsertedRowPB,
+    },
+}
 
 type BlockId = String;
 pub(crate) struct GridBlockManager {
     user: Arc<dyn GridUser>,
     persistence: Arc<BlockIndexCache>,
     block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
+    event_notifier: broadcast::Sender<GridBlockEvent>,
 }
 
 impl GridBlockManager {
@@ -28,6 +51,7 @@ impl GridBlockManager {
         user: &Arc<dyn GridUser>,
         block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
         persistence: Arc<BlockIndexCache>,
+        event_notifier: broadcast::Sender<GridBlockEvent>,
     ) -> FlowyResult<Self> {
         let block_editors = make_block_editors(user, block_meta_revs).await?;
         let user = user.clone();
@@ -35,6 +59,7 @@ impl GridBlockManager {
             user,
             block_editors,
             persistence,
+            event_notifier,
         };
         Ok(manager)
     }
@@ -70,12 +95,12 @@ impl GridBlockManager {
         let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
         let editor = self.get_block_editor(&row_rev.block_id).await?;
 
-        let mut index_row_order = InsertedRowPB::from(&row_rev);
-        let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
-        index_row_order.index = row_index;
-        let changeset = GridBlockChangesetPB::insert(block_id.clone(), vec![index_row_order]);
-        let _ = self.notify_did_update_block(&block_id, changeset).await?;
-        Ok(row_count)
+        let mut row = InsertedRowPB::from(&row_rev);
+        let (number_of_rows, index) = editor.create_row(row_rev, start_row_id).await?;
+        row.index = index;
+
+        let _ = self.event_notifier.send(GridBlockEvent::InsertRow { block_id, row });
+        Ok(number_of_rows)
     }
 
     pub(crate) async fn insert_row(
@@ -84,28 +109,20 @@ impl GridBlockManager {
     ) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
         let mut changesets = vec![];
         for (block_id, row_revs) in rows_by_block_id {
-            let mut inserted_row_orders = vec![];
             let editor = self.get_block_editor(&block_id).await?;
-            let mut row_count = 0;
-            for row in row_revs {
-                let _ = self.persistence.insert(&row.block_id, &row.id)?;
-                let mut row_order = InsertedRowPB::from(&row);
-                let (count, index) = editor.create_row(row, None).await?;
-                row_count = count;
-                row_order.index = index;
-                inserted_row_orders.push(row_order);
+            for row_rev in row_revs {
+                let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
+                let mut row = InsertedRowPB::from(&row_rev);
+                row.index = editor.create_row(row_rev, None).await?.1;
+                let _ = self.event_notifier.send(GridBlockEvent::InsertRow {
+                    block_id: block_id.clone(),
+                    row,
+                });
             }
             changesets.push(GridBlockMetaRevisionChangeset::from_row_count(
                 block_id.clone(),
-                row_count,
+                editor.number_of_rows().await,
             ));
-
-            let _ = self
-                .notify_did_update_block(
-                    &block_id,
-                    GridBlockChangesetPB::insert(block_id.clone(), inserted_row_orders),
-                )
-                .await?;
         }
 
         Ok(changesets)
@@ -118,14 +135,15 @@ impl GridBlockManager {
             None => tracing::error!("Update row failed, can't find the row with id: {}", changeset.row_id),
             Some((_, row_rev)) => {
                 let changed_field_ids = changeset.cell_by_field_id.keys().cloned().collect::<Vec<String>>();
-                let updated_row = UpdatedRowPB {
+                let row = UpdatedRowPB {
                     row: make_row_from_row_rev(row_rev),
                     field_ids: changed_field_ids,
                 };
-                let block_order_changeset = GridBlockChangesetPB::update(&editor.block_id, vec![updated_row]);
-                let _ = self
-                    .notify_did_update_block(&editor.block_id, block_order_changeset)
-                    .await?;
+
+                let _ = self.event_notifier.send(GridBlockEvent::UpdateRow {
+                    block_id: editor.block_id.clone(),
+                    row,
+                });
             }
         }
         Ok(())
@@ -140,28 +158,30 @@ impl GridBlockManager {
             None => Ok(None),
             Some((_, row_rev)) => {
                 let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
-                let _ = self
-                    .notify_did_update_block(
-                        &block_id,
-                        GridBlockChangesetPB::delete(&block_id, vec![row_rev.id.clone()]),
-                    )
-                    .await?;
+                let _ = self.event_notifier.send(GridBlockEvent::DeleteRow {
+                    block_id: editor.block_id.clone(),
+                    row_id: row_rev.id.clone(),
+                });
+
                 Ok(Some(row_rev))
             }
         }
     }
 
-    pub(crate) async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
+    pub(crate) async fn delete_rows(
+        &self,
+        block_rows: Vec<GridBlockRow>,
+    ) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
         let mut changesets = vec![];
-        for grid_block in block_from_row_orders(row_orders) {
-            let editor = self.get_block_editor(&grid_block.id).await?;
-            let row_ids = grid_block
-                .rows
+        for block_row in block_rows {
+            let editor = self.get_block_editor(&block_row.block_id).await?;
+            let row_ids = block_row
+                .row_ids
                 .into_iter()
-                .map(|row_info| Cow::Owned(row_info.row_id().to_owned()))
+                .map(Cow::Owned)
                 .collect::<Vec<Cow<String>>>();
             let row_count = editor.delete_rows(row_ids).await?;
-            let changeset = GridBlockMetaRevisionChangeset::from_row_count(grid_block.id.clone(), row_count);
+            let changeset = GridBlockMetaRevisionChangeset::from_row_count(block_row.block_id, row_count);
             changesets.push(changeset);
         }
 
@@ -179,16 +199,11 @@ impl GridBlockManager {
             is_new: false,
         };
 
-        let notified_changeset = GridBlockChangesetPB {
+        let _ = self.event_notifier.send(GridBlockEvent::Move {
             block_id: editor.block_id.clone(),
-            inserted_rows: vec![insert_row],
-            deleted_rows: vec![delete_row_id],
-            ..Default::default()
-        };
-
-        let _ = self
-            .notify_did_update_block(&editor.block_id, notified_changeset)
-            .await?;
+            deleted_row_id: delete_row_id,
+            inserted_row: insert_row,
+        });
 
         Ok(())
     }
@@ -213,12 +228,13 @@ impl GridBlockManager {
         editor.get_row_rev(row_id).await
     }
 
+    #[allow(dead_code)]
     pub async fn get_row_revs(&self, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
         let editor = self.get_block_editor(block_id).await?;
         editor.get_row_revs::<&str>(None).await
     }
 
-    pub(crate) async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlock>> {
+    pub(crate) async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockRowRevision>> {
         let mut blocks = vec![];
         match block_ids {
             None => {
@@ -226,27 +242,20 @@ impl GridBlockManager {
                     let editor = iter.value();
                     let block_id = editor.block_id.clone();
                     let row_revs = editor.get_row_revs::<&str>(None).await?;
-                    blocks.push(GridBlock { block_id, row_revs });
+                    blocks.push(GridBlockRowRevision { block_id, row_revs });
                 }
             }
             Some(block_ids) => {
                 for block_id in block_ids {
                     let editor = self.get_block_editor(&block_id).await?;
                     let row_revs = editor.get_row_revs::<&str>(None).await?;
-                    blocks.push(GridBlock { block_id, row_revs });
+                    blocks.push(GridBlockRowRevision { block_id, row_revs });
                 }
             }
         }
         Ok(blocks)
     }
 
-    async fn notify_did_update_block(&self, block_id: &str, changeset: GridBlockChangesetPB) -> FlowyResult<()> {
-        send_dart_notification(block_id, GridDartNotification::DidUpdateGridBlock)
-            .payload(changeset)
-            .send();
-        Ok(())
-    }
-
     async fn notify_did_update_cell(&self, changeset: CellChangesetPB) -> FlowyResult<()> {
         let id = format!("{}:{}", changeset.row_id, changeset.field_id);
         send_dart_notification(&id, GridDartNotification::DidUpdateCell).send();

+ 54 - 43
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -1,25 +1,32 @@
 use crate::entities::FieldType;
 use crate::services::cell::{CellBytes, TypeCellData};
 use crate::services::field::*;
+use std::cmp::Ordering;
 use std::fmt::Debug;
 
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use grid_rev_model::{CellRevision, FieldRevision, FieldTypeRevision};
 
 /// This trait is used when doing filter/search on the grid.
-pub trait CellFilterOperation<T> {
-    /// Return true if any_cell_data match the filter condition.
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &T) -> FlowyResult<bool>;
+pub trait CellFilterable<T> {
+    /// Return true if type_cell_data match the filter condition.
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &T) -> FlowyResult<bool>;
 }
 
-pub trait CellGroupOperation {
-    fn apply_group(&self, any_cell_data: TypeCellData, group_content: &str) -> FlowyResult<bool>;
+pub trait CellComparable {
+    fn apply_cmp(&self, type_cell_data: &TypeCellData, other_type_cell_data: &TypeCellData) -> FlowyResult<Ordering>;
 }
 
-/// Return object that describes the cell.
-pub trait CellDisplayable<CD> {
+/// Serialize the cell data in Protobuf/String format.
+///
+/// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
+/// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
+/// list of option ids. So it need to be decoded including convert the option's id to
+/// option's name
+///
+pub trait CellDataSerialize<CD> {
     /// Serialize the cell data into `CellBytes` that will be posted to the `Dart` side. Using the
-    /// corresponding protobuf struct implement in `Dart` to deserialize the data.
+    /// corresponding protobuf struct implemented in `Dart` to deserialize the data.
     ///
     /// Using `utf8` to encode the cell data if the cell data use `String` as its data container.
     /// Using `protobuf` to encode the cell data if the cell data use `Protobuf struct` as its data container.
@@ -43,9 +50,9 @@ pub trait CellDisplayable<CD> {
     ///
     /// returns: Result<CellBytes, FlowyError>
     ///
-    fn displayed_cell_bytes(
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<CD>,
+        cell_data: IntoCellData<CD>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes>;
@@ -55,14 +62,14 @@ pub trait CellDisplayable<CD> {
     /// The cell data is not readable which means it can't display the cell data directly to user.
     /// For example,
     /// 1. the cell data is timestamp if its field type is FieldType::Date that is not readable.
-    /// It needs to be parsed as the date string.
+    /// So it needs to be parsed as the date string with custom format setting.
     ///
     /// 2. the cell data is a commas separated id if its field type if FieldType::MultiSelect that is not readable.
-    /// It needs to be parsed as a commas separated option name.
+    /// So it needs to be parsed as a commas separated option name.
     ///
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<CD>,
+        cell_data: IntoCellData<CD>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<String>;
@@ -76,7 +83,10 @@ pub trait CellDataOperation<CD, CS> {
     /// FieldType::URL => URLCellData
     /// FieldType::Date=> DateCellData
     ///
-    /// Each cell data is a opaque data, it needs to deserialized to a concrete data struct
+    /// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
+    /// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
+    /// list of option ids. So it need to be decoded including convert the option's id to
+    /// option's name
     ///
     /// `cell_data`: the opaque data of the cell.
     /// `decoded_field_type`: the field type of the cell data when doing serialization
@@ -86,7 +96,7 @@ pub trait CellDataOperation<CD, CS> {
     ///
     fn decode_cell_data(
         &self,
-        cell_data: CellData<CD>,
+        cell_data: IntoCellData<CD>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes>;
@@ -130,14 +140,14 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
     Ok(TypeCellData::new(s, field_type).to_json())
 }
 
-pub fn decode_any_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
+pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
     data: T,
     field_rev: &FieldRevision,
 ) -> (FieldType, CellBytes) {
     let to_field_type = field_rev.ty.into();
     match data.try_into() {
-        Ok(any_cell_data) => {
-            let TypeCellData { data, field_type } = any_cell_data;
+        Ok(type_cell_data) => {
+            let TypeCellData { data, field_type } = type_cell_data;
             match try_decode_cell_data(data.into(), &field_type, &to_field_type, field_rev) {
                 Ok(cell_bytes) => (field_type, cell_bytes),
                 Err(e) => {
@@ -156,40 +166,40 @@ pub fn decode_any_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug
     }
 }
 
-pub fn decode_cell_data_to_string(
-    cell_data: CellData<String>,
+pub fn decode_cell_data_to_string<C: Into<IntoCellData<String>>>(
+    cell_data: C,
     from_field_type: &FieldType,
     to_field_type: &FieldType,
     field_rev: &FieldRevision,
 ) -> FlowyResult<String> {
-    let cell_data = cell_data.try_into_inner()?;
+    let cell_data = cell_data.into().try_into_inner()?;
     let get_cell_display_str = || {
         let field_type: FieldTypeRevision = to_field_type.into();
         let result = match to_field_type {
             FieldType::RichText => field_rev
                 .get_type_option::<RichTextTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::Number => field_rev
                 .get_type_option::<NumberTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::DateTime => field_rev
                 .get_type_option::<DateTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::SingleSelect => field_rev
                 .get_type_option::<SingleSelectTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::MultiSelect => field_rev
                 .get_type_option::<MultiSelectTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::Checklist => field_rev
                 .get_type_option::<ChecklistTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::Checkbox => field_rev
                 .get_type_option::<CheckboxTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
             FieldType::URL => field_rev
                 .get_type_option::<URLTypeOptionPB>(field_type)?
-                .displayed_cell_string(cell_data.into(), from_field_type, field_rev),
+                .serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
         };
         Some(result)
     };
@@ -210,7 +220,7 @@ pub fn decode_cell_data_to_string(
 /// and `CellDataOperation` traits.
 ///
 pub fn try_decode_cell_data(
-    cell_data: CellData<String>,
+    cell_data: IntoCellData<String>,
     from_field_type: &FieldType,
     to_field_type: &FieldType,
     field_rev: &FieldRevision,
@@ -312,9 +322,10 @@ pub trait FromCellString {
         Self: Sized;
 }
 
-/// CellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
-pub struct CellData<T>(pub Option<T>);
-impl<T> CellData<T> {
+/// IntoCellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
+///
+pub struct IntoCellData<T>(pub Option<T>);
+impl<T> IntoCellData<T> {
     pub fn try_into_inner(self) -> FlowyResult<T> {
         match self.0 {
             None => Err(ErrorCode::InvalidData.into()),
@@ -323,35 +334,35 @@ impl<T> CellData<T> {
     }
 }
 
-impl<T> std::convert::From<String> for CellData<T>
+impl<T> std::convert::From<String> for IntoCellData<T>
 where
     T: FromCellString,
 {
     fn from(s: String) -> Self {
         match T::from_cell_str(&s) {
-            Ok(inner) => CellData(Some(inner)),
+            Ok(inner) => IntoCellData(Some(inner)),
             Err(e) => {
                 tracing::error!("Deserialize Cell Data failed: {}", e);
-                CellData(None)
+                IntoCellData(None)
             }
         }
     }
 }
 
-impl std::convert::From<usize> for CellData<String> {
+impl std::convert::From<usize> for IntoCellData<String> {
     fn from(n: usize) -> Self {
-        CellData(Some(n.to_string()))
+        IntoCellData(Some(n.to_string()))
     }
 }
 
-impl<T> std::convert::From<T> for CellData<T> {
+impl<T> std::convert::From<T> for IntoCellData<T> {
     fn from(val: T) -> Self {
-        CellData(Some(val))
+        IntoCellData(Some(val))
     }
 }
 
-impl std::convert::From<CellData<String>> for String {
-    fn from(p: CellData<String>) -> Self {
+impl std::convert::From<IntoCellData<String>> for String {
+    fn from(p: IntoCellData<String>) -> Self {
         p.try_into_inner().unwrap_or_else(|_| String::new())
     }
 }

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/cell/mod.rs

@@ -1,5 +1,5 @@
-mod any_cell_data;
 mod cell_operation;
+mod type_cell_data;
 
-pub use any_cell_data::*;
 pub use cell_operation::*;
+pub use type_cell_data::*;

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs → frontend/rust-lib/flowy-grid/src/services/cell/type_cell_data.rs

@@ -1,13 +1,13 @@
 use crate::entities::FieldType;
-use crate::services::cell::{CellData, FromCellString};
+use crate::services::cell::{FromCellString, IntoCellData};
 use bytes::Bytes;
 use flowy_error::{internal_error, FlowyError, FlowyResult};
 use grid_rev_model::CellRevision;
 use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
-/// TypeCellData is a generic CellData, you can parse the cell_data according to the field_type.
-/// When the type of field is changed, it's different from the field_type of AnyCellData.
+/// TypeCellData is a generic CellData, you can parse the type_cell_data according to the field_type.
+/// When the type of field is changed, it's different from the field_type of TypeCellData.
 /// So it will return an empty data. You could check the CellDataOperation trait for more information.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct TypeCellData {
@@ -60,12 +60,12 @@ impl std::convert::TryFrom<CellRevision> for TypeCellData {
     }
 }
 
-impl<T> std::convert::From<TypeCellData> for CellData<T>
+impl<T> std::convert::From<TypeCellData> for IntoCellData<T>
 where
     T: FromCellString,
 {
     fn from(any_call_data: TypeCellData) -> Self {
-        CellData::from(any_call_data.data)
+        IntoCellData::from(any_call_data.data)
     }
 }
 

+ 11 - 11
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_filter.rs

@@ -1,5 +1,5 @@
-use crate::entities::{CheckboxFilterCondition, CheckboxFilterPB};
-use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
+use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB};
+use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
 use crate::services::field::{CheckboxCellData, CheckboxTypeOptionPB};
 use flowy_error::FlowyResult;
 
@@ -7,18 +7,18 @@ impl CheckboxFilterPB {
     pub fn is_visible(&self, cell_data: &CheckboxCellData) -> bool {
         let is_check = cell_data.is_check();
         match self.condition {
-            CheckboxFilterCondition::IsChecked => is_check,
-            CheckboxFilterCondition::IsUnChecked => !is_check,
+            CheckboxFilterConditionPB::IsChecked => is_check,
+            CheckboxFilterConditionPB::IsUnChecked => !is_check,
         }
     }
 }
 
-impl CellFilterOperation<CheckboxFilterPB> for CheckboxTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &CheckboxFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_checkbox() {
+impl CellFilterable<CheckboxFilterPB> for CheckboxTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &CheckboxFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_checkbox() {
             return Ok(true);
         }
-        let cell_data: CellData<CheckboxCellData> = any_cell_data.into();
+        let cell_data: IntoCellData<CheckboxCellData> = type_cell_data.into();
         let checkbox_cell_data = cell_data.try_into_inner()?;
         Ok(filter.is_visible(&checkbox_cell_data))
     }
@@ -26,14 +26,14 @@ impl CellFilterOperation<CheckboxFilterPB> for CheckboxTypeOptionPB {
 
 #[cfg(test)]
 mod tests {
-    use crate::entities::{CheckboxFilterCondition, CheckboxFilterPB};
+    use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB};
     use crate::services::field::CheckboxCellData;
     use std::str::FromStr;
 
     #[test]
     fn checkbox_filter_is_check_test() {
         let checkbox_filter = CheckboxFilterPB {
-            condition: CheckboxFilterCondition::IsChecked,
+            condition: CheckboxFilterConditionPB::IsChecked,
         };
         for (value, visible) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
             let data = CheckboxCellData::from_str(value).unwrap();
@@ -44,7 +44,7 @@ mod tests {
     #[test]
     fn checkbox_filter_is_uncheck_test() {
         let checkbox_filter = CheckboxFilterPB {
-            condition: CheckboxFilterCondition::IsUnChecked,
+            condition: CheckboxFilterConditionPB::IsUnChecked,
         };
         for (value, visible) in [("false", true), ("no", true), ("true", false), ("yes", false)] {
             let data = CheckboxCellData::from_str(value).unwrap();

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::{BoxTypeOptionBuilder, CheckboxCellData, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -42,10 +42,10 @@ pub struct CheckboxTypeOptionPB {
 }
 impl_type_option!(CheckboxTypeOptionPB, FieldType::Checkbox);
 
-impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
-    fn displayed_cell_bytes(
+impl CellDataSerialize<CheckboxCellData> for CheckboxTypeOptionPB {
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<CheckboxCellData>,
+        cell_data: IntoCellData<CheckboxCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -53,9 +53,9 @@ impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
         Ok(CellBytes::new(cell_data))
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<CheckboxCellData>,
+        cell_data: IntoCellData<CheckboxCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
@@ -69,7 +69,7 @@ pub type CheckboxCellChangeset = String;
 impl CellDataOperation<CheckboxCellData, CheckboxCellChangeset> for CheckboxTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<CheckboxCellData>,
+        cell_data: IntoCellData<CheckboxCellData>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -77,7 +77,7 @@ impl CellDataOperation<CheckboxCellData, CheckboxCellChangeset> for CheckboxType
             return Ok(CellBytes::default());
         }
 
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs

@@ -73,7 +73,7 @@ impl CellBytesParser for CheckboxCellDataParser {
     type Object = CheckboxCellData;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
         match String::from_utf8(bytes.to_vec()) {
-            Ok(s) => CheckboxCellData::from_str(&s),
+            Ok(s) => CheckboxCellData::from_cell_str(&s),
             Err(_) => Ok(CheckboxCellData("".to_string())),
         }
     }

+ 21 - 21
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs

@@ -1,5 +1,5 @@
-use crate::entities::{DateFilterCondition, DateFilterPB};
-use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
+use crate::entities::{DateFilterConditionPB, DateFilterPB};
+use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
 use crate::services::field::{DateTimestamp, DateTypeOptionPB};
 use chrono::NaiveDateTime;
 use flowy_error::FlowyResult;
@@ -7,13 +7,13 @@ use flowy_error::FlowyResult;
 impl DateFilterPB {
     pub fn is_visible<T: Into<Option<i64>>>(&self, cell_timestamp: T) -> bool {
         match cell_timestamp.into() {
-            None => DateFilterCondition::DateIsEmpty == self.condition,
+            None => DateFilterConditionPB::DateIsEmpty == self.condition,
             Some(timestamp) => {
                 match self.condition {
-                    DateFilterCondition::DateIsNotEmpty => {
+                    DateFilterConditionPB::DateIsNotEmpty => {
                         return true;
                     }
-                    DateFilterCondition::DateIsEmpty => {
+                    DateFilterConditionPB::DateIsEmpty => {
                         return false;
                     }
                     _ => {}
@@ -45,11 +45,11 @@ impl DateFilterPB {
 
                         // We assume that the cell_timestamp doesn't contain hours, just day.
                         match self.condition {
-                            DateFilterCondition::DateIs => cell_date == expected_date,
-                            DateFilterCondition::DateBefore => cell_date < expected_date,
-                            DateFilterCondition::DateAfter => cell_date > expected_date,
-                            DateFilterCondition::DateOnOrBefore => cell_date <= expected_date,
-                            DateFilterCondition::DateOnOrAfter => cell_date >= expected_date,
+                            DateFilterConditionPB::DateIs => cell_date == expected_date,
+                            DateFilterConditionPB::DateBefore => cell_date < expected_date,
+                            DateFilterConditionPB::DateAfter => cell_date > expected_date,
+                            DateFilterConditionPB::DateOnOrBefore => cell_date <= expected_date,
+                            DateFilterConditionPB::DateOnOrAfter => cell_date >= expected_date,
                             _ => true,
                         }
                     }
@@ -59,12 +59,12 @@ impl DateFilterPB {
     }
 }
 
-impl CellFilterOperation<DateFilterPB> for DateTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &DateFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_date() {
+impl CellFilterable<DateFilterPB> for DateTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &DateFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_date() {
             return Ok(true);
         }
-        let cell_data: CellData<DateTimestamp> = any_cell_data.into();
+        let cell_data: IntoCellData<DateTimestamp> = type_cell_data.into();
         let timestamp = cell_data.try_into_inner()?;
         Ok(filter.is_visible(timestamp))
     }
@@ -73,12 +73,12 @@ impl CellFilterOperation<DateFilterPB> for DateTypeOptionPB {
 #[cfg(test)]
 mod tests {
     #![allow(clippy::all)]
-    use crate::entities::{DateFilterCondition, DateFilterPB};
+    use crate::entities::{DateFilterConditionPB, DateFilterPB};
 
     #[test]
     fn date_filter_is_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateIs,
+            condition: DateFilterConditionPB::DateIs,
             timestamp: Some(1668387885),
             end: None,
             start: None,
@@ -91,7 +91,7 @@ mod tests {
     #[test]
     fn date_filter_before_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateBefore,
+            condition: DateFilterConditionPB::DateBefore,
             timestamp: Some(1668387885),
             start: None,
             end: None,
@@ -105,7 +105,7 @@ mod tests {
     #[test]
     fn date_filter_before_or_on_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateOnOrBefore,
+            condition: DateFilterConditionPB::DateOnOrBefore,
             timestamp: Some(1668387885),
             start: None,
             end: None,
@@ -118,7 +118,7 @@ mod tests {
     #[test]
     fn date_filter_after_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateAfter,
+            condition: DateFilterConditionPB::DateAfter,
             timestamp: Some(1668387885),
             start: None,
             end: None,
@@ -132,7 +132,7 @@ mod tests {
     #[test]
     fn date_filter_within_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateWithIn,
+            condition: DateFilterConditionPB::DateWithIn,
             start: Some(1668272685), // 11/13
             end: Some(1668618285),   // 11/17
             timestamp: None,
@@ -150,7 +150,7 @@ mod tests {
     #[test]
     fn date_filter_is_empty_test() {
         let filter = DateFilterPB {
-            condition: DateFilterCondition::DateIsEmpty,
+            condition: DateFilterConditionPB::DateIsEmpty,
             start: None,
             end: None,
             timestamp: None,

+ 10 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::{
     BoxTypeOptionBuilder, DateCellChangeset, DateCellDataPB, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
 };
@@ -107,21 +107,21 @@ impl DateTypeOptionPB {
     }
 }
 
-impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
-    fn displayed_cell_bytes(
+impl CellDataSerialize<DateTimestamp> for DateTypeOptionPB {
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<DateTimestamp>,
+        cell_data: IntoCellData<DateTimestamp>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
         let timestamp = cell_data.try_into_inner()?;
-        let date_cell_data = self.today_desc_from_timestamp(timestamp);
-        CellBytes::from(date_cell_data)
+        let cell_data_pb = self.today_desc_from_timestamp(timestamp);
+        CellBytes::from(cell_data_pb)
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<DateTimestamp>,
+        cell_data: IntoCellData<DateTimestamp>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
@@ -134,7 +134,7 @@ impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
 impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<DateTimestamp>,
+        cell_data: IntoCellData<DateTimestamp>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -145,7 +145,7 @@ impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
         if !decoded_field_type.is_date() {
             return Ok(CellBytes::default());
         }
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 18 - 18
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_filter.rs

@@ -1,5 +1,5 @@
-use crate::entities::{NumberFilterCondition, NumberFilterPB};
-use crate::services::cell::{CellFilterOperation, TypeCellData};
+use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
+use crate::services::cell::{CellFilterable, TypeCellData};
 use crate::services::field::{NumberCellData, NumberTypeOptionPB};
 use flowy_error::FlowyResult;
 use rust_decimal::prelude::Zero;
@@ -10,10 +10,10 @@ impl NumberFilterPB {
     pub fn is_visible(&self, num_cell_data: &NumberCellData) -> bool {
         if self.content.is_empty() {
             match self.condition {
-                NumberFilterCondition::NumberIsEmpty => {
+                NumberFilterConditionPB::NumberIsEmpty => {
                     return num_cell_data.is_empty();
                 }
-                NumberFilterCondition::NumberIsNotEmpty => {
+                NumberFilterConditionPB::NumberIsNotEmpty => {
                     return !num_cell_data.is_empty();
                 }
                 _ => {}
@@ -24,12 +24,12 @@ impl NumberFilterPB {
             Some(cell_decimal) => {
                 let decimal = Decimal::from_str(&self.content).unwrap_or_else(|_| Decimal::zero());
                 match self.condition {
-                    NumberFilterCondition::Equal => cell_decimal == &decimal,
-                    NumberFilterCondition::NotEqual => cell_decimal != &decimal,
-                    NumberFilterCondition::GreaterThan => cell_decimal > &decimal,
-                    NumberFilterCondition::LessThan => cell_decimal < &decimal,
-                    NumberFilterCondition::GreaterThanOrEqualTo => cell_decimal >= &decimal,
-                    NumberFilterCondition::LessThanOrEqualTo => cell_decimal <= &decimal,
+                    NumberFilterConditionPB::Equal => cell_decimal == &decimal,
+                    NumberFilterConditionPB::NotEqual => cell_decimal != &decimal,
+                    NumberFilterConditionPB::GreaterThan => cell_decimal > &decimal,
+                    NumberFilterConditionPB::LessThan => cell_decimal < &decimal,
+                    NumberFilterConditionPB::GreaterThanOrEqualTo => cell_decimal >= &decimal,
+                    NumberFilterConditionPB::LessThanOrEqualTo => cell_decimal <= &decimal,
                     _ => true,
                 }
             }
@@ -37,13 +37,13 @@ impl NumberFilterPB {
     }
 }
 
-impl CellFilterOperation<NumberFilterPB> for NumberTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &NumberFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_number() {
+impl CellFilterable<NumberFilterPB> for NumberTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &NumberFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_number() {
             return Ok(true);
         }
 
-        let cell_data = any_cell_data.data;
+        let cell_data = type_cell_data.data;
         let num_cell_data = self.format_cell_data(&cell_data)?;
 
         Ok(filter.is_visible(&num_cell_data))
@@ -52,12 +52,12 @@ impl CellFilterOperation<NumberFilterPB> for NumberTypeOptionPB {
 
 #[cfg(test)]
 mod tests {
-    use crate::entities::{NumberFilterCondition, NumberFilterPB};
+    use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
     use crate::services::field::{NumberCellData, NumberFormat};
     #[test]
     fn number_filter_equal_test() {
         let number_filter = NumberFilterPB {
-            condition: NumberFilterCondition::Equal,
+            condition: NumberFilterConditionPB::Equal,
             content: "123".to_owned(),
         };
 
@@ -75,7 +75,7 @@ mod tests {
     #[test]
     fn number_filter_greater_than_test() {
         let number_filter = NumberFilterPB {
-            condition: NumberFilterCondition::GreaterThan,
+            condition: NumberFilterConditionPB::GreaterThan,
             content: "12".to_owned(),
         };
         for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
@@ -87,7 +87,7 @@ mod tests {
     #[test]
     fn number_filter_less_than_test() {
         let number_filter = NumberFilterPB {
-            condition: NumberFilterCondition::LessThan,
+            condition: NumberFilterConditionPB::LessThan,
             content: "100".to_owned(),
         };
         for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", false)] {

+ 8 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

@@ -1,15 +1,13 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::type_options::number_type_option::format::*;
 use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
 use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
-
 use rust_decimal::Decimal;
-
 use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
@@ -105,10 +103,10 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
     s
 }
 
-impl CellDisplayable<String> for NumberTypeOptionPB {
-    fn displayed_cell_bytes(
+impl CellDataSerialize<String> for NumberTypeOptionPB {
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<String>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -119,9 +117,9 @@ impl CellDisplayable<String> for NumberTypeOptionPB {
         }
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<String>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
@@ -135,7 +133,7 @@ pub type NumberCellChangeset = String;
 impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<String>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -143,7 +141,7 @@ impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
             return Ok(CellBytes::default());
         }
 
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs

@@ -99,6 +99,7 @@ impl CellDataIsEmpty for NumberCellData {
         self.decimal.is_none()
     }
 }
+
 pub struct NumberCellDataParser();
 impl CellBytesParser for NumberCellDataParser {
     type Object = NumberCellData;

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs

@@ -1,4 +1,4 @@
-use crate::entities::{ChecklistFilterCondition, ChecklistFilterPB};
+use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB};
 use crate::services::field::{SelectOptionPB, SelectedSelectOptions};
 
 impl ChecklistFilterPB {
@@ -15,7 +15,7 @@ impl ChecklistFilterPB {
             .collect::<Vec<&str>>();
 
         match self.condition {
-            ChecklistFilterCondition::IsComplete => {
+            ChecklistFilterConditionPB::IsComplete => {
                 if selected_option_ids.is_empty() {
                     return false;
                 }
@@ -23,7 +23,7 @@ impl ChecklistFilterPB {
                 all_option_ids.retain(|option_id| !selected_option_ids.contains(option_id));
                 all_option_ids.is_empty()
             }
-            ChecklistFilterCondition::IsIncomplete => {
+            ChecklistFilterConditionPB::IsIncomplete => {
                 if selected_option_ids.is_empty() {
                     return true;
                 }

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
@@ -41,11 +41,11 @@ impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB {
 impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for ChecklistTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
@@ -41,11 +41,11 @@ impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
 impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSelectTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 27 - 27
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs

@@ -1,7 +1,7 @@
 #![allow(clippy::needless_collect)]
 
-use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionCondition, SelectOptionFilterPB};
-use crate::services::cell::{CellFilterOperation, TypeCellData};
+use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
+use crate::services::cell::{CellFilterable, TypeCellData};
 use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
 use crate::services::field::{SelectTypeOptionSharedAction, SelectedSelectOptions};
 use flowy_error::FlowyResult;
@@ -10,7 +10,7 @@ impl SelectOptionFilterPB {
     pub fn is_visible(&self, selected_options: &SelectedSelectOptions, field_type: FieldType) -> bool {
         let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
         match self.condition {
-            SelectOptionCondition::OptionIs => match field_type {
+            SelectOptionConditionPB::OptionIs => match field_type {
                 FieldType::SingleSelect => {
                     if self.option_ids.is_empty() {
                         return true;
@@ -43,7 +43,7 @@ impl SelectOptionFilterPB {
                 }
                 _ => false,
             },
-            SelectOptionCondition::OptionIsNot => match field_type {
+            SelectOptionConditionPB::OptionIsNot => match field_type {
                 FieldType::SingleSelect => {
                     if self.option_ids.is_empty() {
                         return true;
@@ -72,39 +72,39 @@ impl SelectOptionFilterPB {
                 }
                 _ => false,
             },
-            SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
-            SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
+            SelectOptionConditionPB::OptionIsEmpty => selected_option_ids.is_empty(),
+            SelectOptionConditionPB::OptionIsNotEmpty => !selected_option_ids.is_empty(),
         }
     }
 }
 
-impl CellFilterOperation<SelectOptionFilterPB> for MultiSelectTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_multi_select() {
+impl CellFilterable<SelectOptionFilterPB> for MultiSelectTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_multi_select() {
             return Ok(true);
         }
 
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
         Ok(filter.is_visible(&selected_options, FieldType::MultiSelect))
     }
 }
 
-impl CellFilterOperation<SelectOptionFilterPB> for SingleSelectTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_single_select() {
+impl CellFilterable<SelectOptionFilterPB> for SingleSelectTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_single_select() {
             return Ok(true);
         }
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
         Ok(filter.is_visible(&selected_options, FieldType::SingleSelect))
     }
 }
 
-impl CellFilterOperation<ChecklistFilterPB> for ChecklistTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_checklist() {
+impl CellFilterable<ChecklistFilterPB> for ChecklistTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_checklist() {
             return Ok(true);
         }
-        let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
         Ok(filter.is_visible(&self.options, &selected_options))
     }
 }
@@ -112,14 +112,14 @@ impl CellFilterOperation<ChecklistFilterPB> for ChecklistTypeOptionPB {
 #[cfg(test)]
 mod tests {
     #![allow(clippy::all)]
-    use crate::entities::{FieldType, SelectOptionCondition, SelectOptionFilterPB};
+    use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
     use crate::services::field::selection_type_option::{SelectOptionPB, SelectedSelectOptions};
 
     #[test]
     fn select_option_filter_is_empty_test() {
         let option = SelectOptionPB::new("A");
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIsEmpty,
+            condition: SelectOptionConditionPB::OptionIsEmpty,
             option_ids: vec![],
         };
 
@@ -152,7 +152,7 @@ mod tests {
         let option_1 = SelectOptionPB::new("A");
         let option_2 = SelectOptionPB::new("B");
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIsNotEmpty,
+            condition: SelectOptionConditionPB::OptionIsNotEmpty,
             option_ids: vec![option_1.id.clone(), option_2.id.clone()],
         };
 
@@ -191,7 +191,7 @@ mod tests {
         let option_2 = SelectOptionPB::new("B");
         let option_3 = SelectOptionPB::new("C");
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIsNot,
+            condition: SelectOptionConditionPB::OptionIsNot,
             option_ids: vec![option_1.id.clone(), option_2.id.clone()],
         };
 
@@ -215,7 +215,7 @@ mod tests {
         let option_3 = SelectOptionPB::new("c");
 
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIs,
+            condition: SelectOptionConditionPB::OptionIs,
             option_ids: vec![option_1.id.clone()],
         };
         for (options, is_visible) in vec![
@@ -237,7 +237,7 @@ mod tests {
         let option_2 = SelectOptionPB::new("B");
 
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIs,
+            condition: SelectOptionConditionPB::OptionIs,
             option_ids: vec![],
         };
         for (options, is_visible) in vec![
@@ -258,7 +258,7 @@ mod tests {
         let option_2 = SelectOptionPB::new("B");
         let option_3 = SelectOptionPB::new("C");
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIsNot,
+            condition: SelectOptionConditionPB::OptionIsNot,
             option_ids: vec![option_1.id.clone(), option_2.id.clone()],
         };
 
@@ -283,7 +283,7 @@ mod tests {
         let option_3 = SelectOptionPB::new("C");
 
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIs,
+            condition: SelectOptionConditionPB::OptionIs,
             option_ids: vec![option_1.id.clone(), option_2.id.clone()],
         };
         for (options, is_visible) in vec![
@@ -305,7 +305,7 @@ mod tests {
         let option_1 = SelectOptionPB::new("A");
 
         let filter = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIs,
+            condition: SelectOptionConditionPB::OptionIs,
             option_ids: vec![],
         };
         for (options, is_visible) in vec![(vec![option_1.clone()], true), (vec![], true)] {

+ 15 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs

@@ -1,7 +1,7 @@
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
 use crate::services::cell::{
-    CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString,
+    CellBytes, CellBytesParser, CellDataIsEmpty, CellDataSerialize, FromCellChangeset, FromCellString, IntoCellData,
 };
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
@@ -69,7 +69,10 @@ impl std::default::Default for SelectOptionColorPB {
     }
 }
 
-pub fn make_selected_options(cell_data: CellData<SelectOptionIds>, options: &[SelectOptionPB]) -> Vec<SelectOptionPB> {
+pub fn make_selected_options(
+    cell_data: IntoCellData<SelectOptionIds>,
+    options: &[SelectOptionPB],
+) -> Vec<SelectOptionPB> {
     if let Ok(ids) = cell_data.try_into_inner() {
         ids.iter()
             .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
@@ -110,7 +113,7 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
     }
 
     /// Return a list of options that are selected by user
-    fn get_selected_options(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB {
+    fn get_selected_options(&self, cell_data: IntoCellData<SelectOptionIds>) -> SelectOptionCellDataPB {
         let mut select_options = make_selected_options(cell_data, self.options());
         match self.number_of_max_options() {
             None => {}
@@ -126,7 +129,7 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
 
     fn transform_cell_data(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -150,7 +153,9 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
                     })
                 });
 
-                return CellBytes::from(self.get_selected_options(CellData(Some(SelectOptionIds(transformed_ids)))));
+                return CellBytes::from(
+                    self.get_selected_options(IntoCellData(Some(SelectOptionIds(transformed_ids)))),
+                );
             }
             _ => {
                 return Ok(CellBytes::default());
@@ -165,13 +170,13 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
     fn mut_options(&mut self) -> &mut Vec<SelectOptionPB>;
 }
 
-impl<T> CellDisplayable<SelectOptionIds> for T
+impl<T> CellDataSerialize<SelectOptionIds> for T
 where
     T: SelectTypeOptionSharedAction,
 {
-    fn displayed_cell_bytes(
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -183,9 +188,9 @@ where
         )
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::field::{
@@ -40,11 +40,11 @@ impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
 impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSelectTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
     }
 
     fn apply_changeset(

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs

@@ -1,5 +1,5 @@
 use crate::entities::FieldType;
-use crate::services::cell::{CellBytes, CellData};
+use crate::services::cell::{CellBytes, IntoCellData};
 use crate::services::field::{
     MultiSelectTypeOptionPB, SelectOptionColorPB, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
     SingleSelectTypeOptionPB, CHECK, UNCHECK,
@@ -57,7 +57,7 @@ impl SelectOptionTypeOptionTransformer {
 
     pub fn transform_type_option_cell_data<T>(
         shared: &T,
-        cell_data: CellData<SelectOptionIds>,
+        cell_data: IntoCellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes>
@@ -78,7 +78,7 @@ impl SelectOptionTypeOptionTransformer {
                         transformed_ids.push(option.id.clone());
                     }
                 });
-                let transformed_cell_data = CellData::from(SelectOptionIds::from(transformed_ids));
+                let transformed_cell_data = IntoCellData::from(SelectOptionIds::from(transformed_ids));
                 CellBytes::from(shared.get_selected_options(transformed_cell_data))
             }
             _ => Ok(CellBytes::default()),

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

@@ -1,5 +1,5 @@
-use crate::entities::{TextFilterCondition, TextFilterPB};
-use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
+use crate::entities::{TextFilterConditionPB, TextFilterPB};
+use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
 use crate::services::field::{RichTextTypeOptionPB, TextCellData};
 use flowy_error::FlowyResult;
 
@@ -8,25 +8,25 @@ impl TextFilterPB {
         let cell_data = cell_data.as_ref().to_lowercase();
         let content = &self.content.to_lowercase();
         match self.condition {
-            TextFilterCondition::Is => &cell_data == content,
-            TextFilterCondition::IsNot => &cell_data != content,
-            TextFilterCondition::Contains => cell_data.contains(content),
-            TextFilterCondition::DoesNotContain => !cell_data.contains(content),
-            TextFilterCondition::StartsWith => cell_data.starts_with(content),
-            TextFilterCondition::EndsWith => cell_data.ends_with(content),
-            TextFilterCondition::TextIsEmpty => cell_data.is_empty(),
-            TextFilterCondition::TextIsNotEmpty => !cell_data.is_empty(),
+            TextFilterConditionPB::Is => &cell_data == content,
+            TextFilterConditionPB::IsNot => &cell_data != content,
+            TextFilterConditionPB::Contains => cell_data.contains(content),
+            TextFilterConditionPB::DoesNotContain => !cell_data.contains(content),
+            TextFilterConditionPB::StartsWith => cell_data.starts_with(content),
+            TextFilterConditionPB::EndsWith => cell_data.ends_with(content),
+            TextFilterConditionPB::TextIsEmpty => cell_data.is_empty(),
+            TextFilterConditionPB::TextIsNotEmpty => !cell_data.is_empty(),
         }
     }
 }
 
-impl CellFilterOperation<TextFilterPB> for RichTextTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_text() {
+impl CellFilterable<TextFilterPB> for RichTextTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_text() {
             return Ok(false);
         }
 
-        let cell_data: CellData<TextCellData> = any_cell_data.into();
+        let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
         let text_cell_data = cell_data.try_into_inner()?;
         Ok(filter.is_visible(text_cell_data))
     }
@@ -34,12 +34,12 @@ impl CellFilterOperation<TextFilterPB> for RichTextTypeOptionPB {
 #[cfg(test)]
 mod tests {
     #![allow(clippy::all)]
-    use crate::entities::{TextFilterCondition, TextFilterPB};
+    use crate::entities::{TextFilterConditionPB, TextFilterPB};
 
     #[test]
     fn text_filter_equal_test() {
         let text_filter = TextFilterPB {
-            condition: TextFilterCondition::Is,
+            condition: TextFilterConditionPB::Is,
             content: "appflowy".to_owned(),
         };
 
@@ -51,7 +51,7 @@ mod tests {
     #[test]
     fn text_filter_start_with_test() {
         let text_filter = TextFilterPB {
-            condition: TextFilterCondition::StartsWith,
+            condition: TextFilterConditionPB::StartsWith,
             content: "appflowy".to_owned(),
         };
 
@@ -63,7 +63,7 @@ mod tests {
     #[test]
     fn text_filter_end_with_test() {
         let text_filter = TextFilterPB {
-            condition: TextFilterCondition::EndsWith,
+            condition: TextFilterConditionPB::EndsWith,
             content: "appflowy".to_owned(),
         };
 
@@ -74,7 +74,7 @@ mod tests {
     #[test]
     fn text_filter_empty_test() {
         let text_filter = TextFilterPB {
-            condition: TextFilterCondition::TextIsEmpty,
+            condition: TextFilterConditionPB::TextIsEmpty,
             content: "appflowy".to_owned(),
         };
 
@@ -84,7 +84,7 @@ mod tests {
     #[test]
     fn text_filter_contain_test() {
         let text_filter = TextFilterPB {
-            condition: TextFilterCondition::Contains,
+            condition: TextFilterConditionPB::Contains,
             content: "appflowy".to_owned(),
         };
 

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

@@ -1,8 +1,8 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{
-    decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellData, CellDataIsEmpty,
-    CellDataOperation, CellDisplayable, FromCellString,
+    decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellDataIsEmpty, CellDataOperation,
+    CellDataSerialize, FromCellString, IntoCellData,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
@@ -39,32 +39,32 @@ pub struct RichTextTypeOptionPB {
 }
 impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
 
-impl CellDisplayable<String> for RichTextTypeOptionPB {
-    fn displayed_cell_bytes(
+impl CellDataSerialize<RichTextCellData> for RichTextTypeOptionPB {
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<RichTextCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        let cell_str: String = cell_data.try_into_inner()?;
+        let cell_str: RichTextCellData = cell_data.try_into_inner()?;
         Ok(CellBytes::new(cell_str))
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<RichTextCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let cell_str: String = cell_data.try_into_inner()?;
+        let cell_str: RichTextCellData = cell_data.try_into_inner()?;
         Ok(cell_str)
     }
 }
 
-impl CellDataOperation<String, String> for RichTextTypeOptionPB {
+impl CellDataOperation<RichTextCellData, String> for RichTextTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<String>,
+        cell_data: IntoCellData<RichTextCellData>,
         decoded_field_type: &FieldType,
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
@@ -77,7 +77,7 @@ impl CellDataOperation<String, String> for RichTextTypeOptionPB {
             let s = decode_cell_data_to_string(cell_data, decoded_field_type, decoded_field_type, field_rev);
             Ok(CellBytes::new(s.unwrap_or_else(|_| "".to_owned())))
         } else {
-            self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+            self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
         }
     }
 
@@ -141,3 +141,5 @@ impl CellBytesParser for TextCellDataParser {
         }
     }
 }
+
+pub type RichTextCellData = String;

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_filter.rs

@@ -1,15 +1,15 @@
 use crate::entities::TextFilterPB;
-use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
+use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
 use crate::services::field::{TextCellData, URLTypeOptionPB};
 use flowy_error::FlowyResult;
 
-impl CellFilterOperation<TextFilterPB> for URLTypeOptionPB {
-    fn apply_filter(&self, any_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
-        if !any_cell_data.is_url() {
+impl CellFilterable<TextFilterPB> for URLTypeOptionPB {
+    fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
+        if !type_cell_data.is_url() {
             return Ok(true);
         }
 
-        let cell_data: CellData<TextCellData> = any_cell_data.into();
+        let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
         let text_cell_data = cell_data.try_into_inner()?;
         Ok(filter.is_visible(&text_cell_data))
     }

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_tests.rs

@@ -1,9 +1,9 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::{CellData, CellDataOperation};
+    use crate::services::cell::{CellDataOperation, IntoCellData};
     use crate::services::field::{FieldBuilder, URLCellDataParser};
-    use crate::services::field::{URLCellDataPB, URLTypeOptionPB};
+    use crate::services::field::{URLCellData, URLTypeOptionPB};
     use grid_rev_model::FieldRevision;
 
     /// The expected_str will equal to the input string, but the expected_url will be empty if there's no
@@ -175,12 +175,12 @@ mod tests {
         assert_eq!(expected_url.to_owned(), decode_cell_data.url);
     }
 
-    fn decode_cell_data<T: Into<CellData<URLCellDataPB>>>(
+    fn decode_cell_data<T: Into<IntoCellData<URLCellData>>>(
         encoded_data: T,
         type_option: &URLTypeOptionPB,
         field_rev: &FieldRevision,
         field_type: &FieldType,
-    ) -> URLCellDataPB {
+    ) -> URLCellData {
         type_option
             .decode_cell_data(encoded_data.into(), field_type, field_rev)
             .unwrap()

+ 16 - 15
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs

@@ -1,7 +1,7 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellDataPB};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData, URLCellDataPB};
 use bytes::Bytes;
 use fancy_regex::Regex;
 use flowy_derive::ProtoBuf;
@@ -36,41 +36,42 @@ pub struct URLTypeOptionPB {
 }
 impl_type_option!(URLTypeOptionPB, FieldType::URL);
 
-impl CellDisplayable<URLCellDataPB> for URLTypeOptionPB {
-    fn displayed_cell_bytes(
+impl CellDataSerialize<URLCellData> for URLTypeOptionPB {
+    fn serialize_cell_data_to_bytes(
         &self,
-        cell_data: CellData<URLCellDataPB>,
+        cell_data: IntoCellData<URLCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
-        CellBytes::from(cell_data)
+        let cell_data_pb: URLCellDataPB = cell_data.try_into_inner()?.into();
+        CellBytes::from(cell_data_pb)
     }
 
-    fn displayed_cell_string(
+    fn serialize_cell_data_to_str(
         &self,
-        cell_data: CellData<URLCellDataPB>,
+        cell_data: IntoCellData<URLCellData>,
         _decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
-        let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
+        let cell_data: URLCellData = cell_data.try_into_inner()?;
         Ok(cell_data.content)
     }
 }
 
 pub type URLCellChangeset = String;
 
-impl CellDataOperation<URLCellDataPB, URLCellChangeset> for URLTypeOptionPB {
+impl CellDataOperation<URLCellData, URLCellChangeset> for URLTypeOptionPB {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<URLCellDataPB>,
+        cell_data: IntoCellData<URLCellData>,
         decoded_field_type: &FieldType,
-        field_rev: &FieldRevision,
+        _field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
         if !decoded_field_type.is_url() {
             return Ok(CellBytes::default());
         }
-        self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
+        let cell_data = cell_data.try_into_inner()?.to_json()?;
+        Ok(CellBytes::new(cell_data))
     }
 
     fn apply_changeset(
@@ -83,7 +84,7 @@ impl CellDataOperation<URLCellDataPB, URLCellChangeset> for URLTypeOptionPB {
         if let Ok(Some(m)) = URL_REGEX.find(&content) {
             url = auto_append_scheme(m.as_str());
         }
-        URLCellDataPB { url, content }.to_json()
+        URLCellData { url, content }.to_json()
     }
 }
 

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

@@ -4,7 +4,7 @@ use flowy_derive::ProtoBuf;
 use flowy_error::{internal_error, FlowyResult};
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
+#[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct URLCellDataPB {
     #[pb(index = 1)]
     pub url: String,
@@ -13,7 +13,22 @@ pub struct URLCellDataPB {
     pub content: String,
 }
 
-impl URLCellDataPB {
+impl From<URLCellData> for URLCellDataPB {
+    fn from(data: URLCellData) -> Self {
+        Self {
+            url: data.url,
+            content: data.content,
+        }
+    }
+}
+
+#[derive(Clone, Default, Serialize, Deserialize)]
+pub struct URLCellData {
+    pub url: String,
+    pub content: String,
+}
+
+impl URLCellData {
     pub fn new(s: &str) -> Self {
         Self {
             url: "".to_string(),
@@ -26,7 +41,7 @@ impl URLCellDataPB {
     }
 }
 
-impl CellDataIsEmpty for URLCellDataPB {
+impl CellDataIsEmpty for URLCellData {
     fn is_empty(&self) -> bool {
         self.content.is_empty()
     }
@@ -34,15 +49,18 @@ impl CellDataIsEmpty for URLCellDataPB {
 
 pub struct URLCellDataParser();
 impl CellBytesParser for URLCellDataParser {
-    type Object = URLCellDataPB;
+    type Object = URLCellData;
 
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
-        URLCellDataPB::try_from(bytes.as_ref()).map_err(internal_error)
+        match String::from_utf8(bytes.to_vec()) {
+            Ok(s) => URLCellData::from_cell_str(&s),
+            Err(_) => Ok(URLCellData::default()),
+        }
     }
 }
 
-impl FromCellString for URLCellDataPB {
+impl FromCellString for URLCellData {
     fn from_cell_str(s: &str) -> FlowyResult<Self> {
-        serde_json::from_str::<URLCellDataPB>(s).map_err(internal_error)
+        serde_json::from_str::<URLCellData>(s).map_err(internal_error)
     }
 }

+ 20 - 23
frontend/rust-lib/flowy-grid/src/services/filter/controller.rs

@@ -1,9 +1,9 @@
 use crate::entities::filter_entities::*;
 use crate::entities::{FieldType, InsertedRowPB, RowPB};
-use crate::services::cell::{CellFilterOperation, TypeCellData};
+use crate::services::cell::{CellFilterable, TypeCellData};
 use crate::services::field::*;
 use crate::services::filter::{FilterChangeset, FilterMap, FilterResult, FilterResultNotification, FilterType};
-use crate::services::row::GridBlock;
+use crate::services::row::GridBlockRowRevision;
 use crate::services::view_editor::{GridViewChanged, GridViewChangedNotifier};
 use flowy_error::FlowyResult;
 use flowy_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
@@ -20,7 +20,7 @@ pub trait FilterDelegate: Send + Sync + 'static {
     fn get_filter_rev(&self, filter_type: FilterType) -> Fut<Option<Arc<FilterRevision>>>;
     fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
     fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
-    fn get_blocks(&self) -> Fut<Vec<GridBlock>>;
+    fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>>;
     fn get_row_rev(&self, rows_id: &str) -> Fut<Option<(usize, Arc<RowRevision>)>>;
 }
 
@@ -44,7 +44,7 @@ impl FilterController {
         notifier: GridViewChangedNotifier,
     ) -> Self
     where
-        T: FilterDelegate,
+        T: FilterDelegate + 'static,
     {
         let mut this = Self {
             view_id: view_id.to_string(),
@@ -55,7 +55,7 @@ impl FilterController {
             task_scheduler,
             notifier,
         };
-        this.cache_filters(filter_revs).await;
+        this.refresh_filters(filter_revs).await;
         this
     }
 
@@ -191,17 +191,14 @@ impl FilterController {
     }
 
     #[tracing::instrument(level = "trace", skip(self))]
-    pub async fn did_receive_filter_changed(
-        &mut self,
-        changeset: FilterChangeset,
-    ) -> Option<FilterChangesetNotificationPB> {
+    pub async fn did_receive_changes(&mut self, changeset: FilterChangeset) -> Option<FilterChangesetNotificationPB> {
         let mut notification: Option<FilterChangesetNotificationPB> = None;
         if let Some(filter_type) = &changeset.insert_filter {
             if let Some(filter) = self.filter_from_filter_type(filter_type).await {
                 notification = Some(FilterChangesetNotificationPB::from_insert(&self.view_id, vec![filter]));
             }
             if let Some(filter_rev) = self.delegate.get_filter_rev(filter_type.clone()).await {
-                let _ = self.cache_filters(vec![filter_rev]).await;
+                let _ = self.refresh_filters(vec![filter_rev]).await;
             }
         }
 
@@ -218,7 +215,7 @@ impl FilterController {
 
                 // Update the corresponding filter in the cache
                 if let Some(filter_rev) = self.delegate.get_filter_rev(updated_filter_type.new.clone()).await {
-                    let _ = self.cache_filters(vec![filter_rev]).await;
+                    let _ = self.refresh_filters(vec![filter_rev]).await;
                 }
 
                 if let Some(filter_id) = filter_id {
@@ -253,7 +250,7 @@ impl FilterController {
     }
 
     #[tracing::instrument(level = "trace", skip_all)]
-    async fn cache_filters(&mut self, filter_revs: Vec<Arc<FilterRevision>>) {
+    async fn refresh_filters(&mut self, filter_revs: Vec<Arc<FilterRevision>>) {
         for filter_rev in filter_revs {
             if let Some(field_rev) = self.delegate.get_field_rev(&filter_rev.field_id).await {
                 let filter_type = FilterType::from(&field_rev);
@@ -355,7 +352,7 @@ fn filter_cell(
     filter_map: &FilterMap,
     cell_rev: Option<&CellRevision>,
 ) -> Option<bool> {
-    let any_cell_data = match cell_rev {
+    let type_cell_data = match cell_rev {
         None => TypeCellData::from_field_type(&filter_id.field_type),
         Some(cell_rev) => match TypeCellData::try_from(cell_rev) {
             Ok(cell_data) => cell_data,
@@ -365,13 +362,13 @@ fn filter_cell(
             }
         },
     };
-    let cloned_cell_data = any_cell_data.data.clone();
+    let cloned_type_cell_data = type_cell_data.data.clone();
     let is_visible = match &filter_id.field_type {
         FieldType::RichText => filter_map.text_filter.get(filter_id).and_then(|filter| {
             Some(
                 field_rev
                     .get_type_option::<RichTextTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -379,7 +376,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<NumberTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -387,7 +384,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<DateTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -395,7 +392,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<SingleSelectTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -403,7 +400,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<MultiSelectTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -411,7 +408,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<CheckboxTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -419,7 +416,7 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<URLTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
@@ -427,14 +424,14 @@ fn filter_cell(
             Some(
                 field_rev
                     .get_type_option::<ChecklistTypeOptionPB>(field_rev.ty)?
-                    .apply_filter(any_cell_data, filter)
+                    .apply_filter(type_cell_data, filter)
                     .ok(),
             )
         }),
     }?;
     tracing::Span::current().record(
         "cell_content",
-        &format!("{} => {:?}", cloned_cell_data, is_visible.unwrap()).as_str(),
+        &format!("{} => {:?}", cloned_type_cell_data, is_visible.unwrap()).as_str(),
     );
     is_visible
 }

+ 3 - 4
frontend/rust-lib/flowy-grid/src/services/filter/entities.rs

@@ -71,12 +71,11 @@ pub struct FilterType {
     pub field_type: FieldType,
 }
 
-impl FilterType {
-    pub fn field_type_rev(&self) -> FieldTypeRevision {
-        self.field_type.clone().into()
+impl From<FilterType> for FieldTypeRevision {
+    fn from(filter_type: FilterType) -> Self {
+        filter_type.field_type.into()
     }
 }
-
 impl std::convert::From<&Arc<FieldRevision>> for FilterType {
     fn from(rev: &Arc<FieldRevision>) -> Self {
         Self {

+ 34 - 25
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -3,7 +3,7 @@ use crate::entities::CellPathParams;
 use crate::entities::*;
 use crate::manager::GridUser;
 use crate::services::block_manager::GridBlockManager;
-use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes};
+use crate::services::cell::{apply_cell_data_changeset, decode_type_cell_data, CellBytes};
 use crate::services::field::{
     default_type_option_builder_from_type, type_option_builder_from_bytes, type_option_builder_from_json_str,
     FieldBuilder,
@@ -12,7 +12,7 @@ use crate::services::field::{
 use crate::services::filter::FilterType;
 use crate::services::grid_editor_trait_impl::GridViewEditorDelegateImpl;
 use crate::services::persistence::block_index::BlockIndexCache;
-use crate::services::row::{GridBlock, RowRevisionBuilder};
+use crate::services::row::{GridBlockRow, GridBlockRowRevision, RowRevisionBuilder};
 use crate::services::view_editor::{GridViewChanged, GridViewManager};
 use bytes::Bytes;
 use flowy_database::ConnectionPool;
@@ -63,8 +63,9 @@ impl GridRevisionEditor {
         let grid_pad = Arc::new(RwLock::new(grid_pad));
 
         // Block manager
+        let (block_event_tx, block_event_rx) = broadcast::channel(100);
         let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
-        let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence).await?);
+        let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence, block_event_tx).await?);
         let delegate = Arc::new(GridViewEditorDelegateImpl {
             pad: grid_pad.clone(),
             block_manager: block_manager.clone(),
@@ -72,7 +73,8 @@ impl GridRevisionEditor {
         });
 
         // View manager
-        let view_manager = Arc::new(GridViewManager::new(grid_id.to_owned(), user.clone(), delegate).await?);
+        let view_manager =
+            Arc::new(GridViewManager::new(grid_id.to_owned(), user.clone(), delegate, block_event_rx).await?);
 
         let editor = Arc::new(Self {
             grid_id: grid_id.to_owned(),
@@ -391,11 +393,12 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    pub async fn get_row_pbs(&self, block_id: &str) -> FlowyResult<Vec<RowPB>> {
-        let rows = self.block_manager.get_row_revs(block_id).await?;
+    /// Returns all the rows in this block.
+    pub async fn get_row_pbs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<RowPB>> {
+        let rows = self.view_manager.get_row_revs(view_id, block_id).await?;
         let rows = self
             .view_manager
-            .filter_rows(block_id, rows)
+            .filter_rows(view_id, block_id, rows)
             .await?
             .into_iter()
             .map(|row_rev| RowPB::from(&row_rev))
@@ -428,20 +431,20 @@ impl GridRevisionEditor {
     }
 
     pub async fn get_cell(&self, params: &CellPathParams) -> Option<CellPB> {
-        let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
+        let (field_type, cell_bytes) = self.decode_cell_data_from(params).await?;
         Some(CellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
     }
 
     pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellBytes> {
-        let (_, cell_data) = self.decode_any_cell_data(params).await?;
+        let (_, cell_data) = self.decode_cell_data_from(params).await?;
         Some(cell_data)
     }
 
-    async fn decode_any_cell_data(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
+    async fn decode_cell_data_from(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
         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()??;
         let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
-        Some(decode_any_cell_data(cell_rev.data, &field_rev))
+        Some(decode_type_cell_data(cell_rev.data, &field_rev))
     }
 
     pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellRevision>> {
@@ -508,7 +511,7 @@ impl GridRevisionEditor {
         Ok(block_meta_revs)
     }
 
-    pub async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlock>> {
+    pub async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockRowRevision>> {
         let block_ids = match block_ids {
             None => self
                 .grid_pad
@@ -524,32 +527,28 @@ impl GridRevisionEditor {
         Ok(blocks)
     }
 
-    pub async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<()> {
-        let changesets = self.block_manager.delete_rows(row_orders).await?;
+    pub async fn delete_rows(&self, block_rows: Vec<GridBlockRow>) -> FlowyResult<()> {
+        let changesets = self.block_manager.delete_rows(block_rows).await?;
         for changeset in changesets {
             let _ = self.update_block(changeset).await?;
         }
         Ok(())
     }
 
-    pub async fn get_grid(&self) -> FlowyResult<GridPB> {
+    pub async fn get_grid(&self, view_id: &str) -> FlowyResult<GridPB> {
         let pad = self.grid_pad.read().await;
         let fields = pad.get_field_revs(None)?.iter().map(FieldIdPB::from).collect();
-
-        let mut blocks = vec![];
+        let mut all_rows = vec![];
         for block_rev in pad.get_block_meta_revs() {
-            let rows = self.get_row_pbs(&block_rev.block_id).await?;
-            let block = BlockPB {
-                id: block_rev.block_id.clone(),
-                rows,
-            };
-            blocks.push(block);
+            if let Ok(rows) = self.get_row_pbs(view_id, &block_rev.block_id).await {
+                all_rows.extend(rows);
+            }
         }
 
         Ok(GridPB {
             id: self.grid_id.clone(),
             fields,
-            blocks,
+            rows: all_rows,
         })
     }
 
@@ -589,6 +588,16 @@ impl GridRevisionEditor {
         Ok(())
     }
 
+    pub async fn delete_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
+        let _ = self.view_manager.delete_sort(params).await?;
+        Ok(())
+    }
+
+    pub async fn create_or_update_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
+        let _ = self.view_manager.create_or_update_sort(params).await?;
+        Ok(())
+    }
+
     pub async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
         let MoveRowParams {
             view_id: _,
@@ -801,7 +810,7 @@ impl GridRevisionEditor {
     }
 
     async fn notify_did_update_grid(&self, changeset: GridFieldChangesetPB) -> FlowyResult<()> {
-        send_dart_notification(&self.grid_id, GridDartNotification::DidUpdateGridField)
+        send_dart_notification(&self.grid_id, GridDartNotification::DidUpdateGridFields)
             .payload(changeset)
             .send();
         Ok(())

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

@@ -1,5 +1,5 @@
 use crate::services::block_manager::GridBlockManager;
-use crate::services::row::GridBlock;
+use crate::services::row::GridBlockRowRevision;
 use crate::services::view_editor::GridViewEditorDelegate;
 use flowy_sync::client_grid::GridRevisionPad;
 use flowy_task::TaskDispatcher;
@@ -51,11 +51,11 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
         })
     }
 
-    fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>> {
+    fn get_row_revs(&self, block_id: Option<Vec<String>>) -> Fut<Vec<Arc<RowRevision>>> {
         let block_manager = self.block_manager.clone();
 
         to_fut(async move {
-            let blocks = block_manager.get_blocks(None).await.unwrap();
+            let blocks = block_manager.get_blocks(block_id).await.unwrap();
             blocks
                 .into_iter()
                 .flat_map(|block| block.row_revs)
@@ -63,7 +63,7 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
         })
     }
 
-    fn get_blocks(&self) -> Fut<Vec<GridBlock>> {
+    fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>> {
         let block_manager = self.block_manager.clone();
         to_fut(async move { block_manager.get_blocks(None).await.unwrap_or_default() })
     }

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -1,5 +1,5 @@
 use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
-use crate::services::cell::{decode_any_cell_data, CellBytesParser, CellDataIsEmpty};
+use crate::services::cell::{decode_type_cell_data, CellBytesParser, CellDataIsEmpty};
 use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions};
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::entities::Group;
@@ -184,7 +184,7 @@ where
 
             if let Some(cell_rev) = cell_rev {
                 let mut grouped_rows: Vec<GroupedRow> = vec![];
-                let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev).1;
+                let cell_bytes = decode_type_cell_data(cell_rev.data, field_rev).1;
                 let cell_data = cell_bytes.parser::<P>()?;
                 for group in self.group_ctx.groups() {
                     if self.can_group(&group.filter_content, &cell_data) {
@@ -224,7 +224,7 @@ where
         field_rev: &FieldRevision,
     ) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
         if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
-            let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
+            let cell_bytes = decode_type_cell_data(cell_rev.data.clone(), field_rev).1;
             let cell_data = cell_bytes.parser::<P>()?;
             let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &cell_data);
 
@@ -247,7 +247,7 @@ where
     ) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
         // if the cell_rev is none, then the row must in the default group.
         if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
-            let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
+            let cell_bytes = decode_type_cell_data(cell_rev.data.clone(), field_rev).1;
             let cell_data = cell_bytes.parser::<P>()?;
             if !cell_data.is_empty() {
                 tracing::error!("did_delete_delete_row {:?}", cell_rev.data);
@@ -280,7 +280,7 @@ where
         };
 
         if let Some(cell_rev) = cell_rev {
-            let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev).1;
+            let cell_bytes = decode_type_cell_data(cell_rev.data, context.field_rev).1;
             let cell_data = cell_bytes.parser::<P>()?;
             Ok(self.move_row(&cell_data, context))
         } else {

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

@@ -11,4 +11,5 @@ pub mod group;
 pub mod persistence;
 pub mod row;
 pub mod setting;
+pub mod sort;
 pub mod view_editor;

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/row/mod.rs

@@ -2,4 +2,4 @@ mod row_builder;
 mod row_loader;
 
 pub use row_builder::*;
-pub(crate) use row_loader::*;
+pub use row_loader::*;

+ 10 - 41
frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs

@@ -1,42 +1,22 @@
-use crate::entities::{BlockPB, RepeatedBlockPB, RowPB};
-
+use crate::entities::RowPB;
 use grid_rev_model::RowRevision;
-use std::collections::HashMap;
+
 use std::sync::Arc;
 
-pub struct GridBlock {
+pub struct GridBlockRowRevision {
     pub(crate) block_id: String,
     pub row_revs: Vec<Arc<RowRevision>>,
 }
 
-pub(crate) fn block_from_row_orders(row_orders: Vec<RowPB>) -> Vec<BlockPB> {
-    let mut map: HashMap<String, BlockPB> = HashMap::new();
-    row_orders.into_iter().for_each(|row_info| {
-        // Memory Optimization: escape clone block_id
-        let block_id = row_info.block_id().to_owned();
-        let cloned_block_id = block_id.clone();
-        map.entry(block_id)
-            .or_insert_with(|| BlockPB::new(&cloned_block_id, vec![]))
-            .rows
-            .push(row_info);
-    });
-    map.into_values().collect::<Vec<_>>()
+pub struct GridBlockRow {
+    pub block_id: String,
+    pub row_ids: Vec<String>,
 }
-//
-// #[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_pb_from_row_rev(row_revs: &[Arc<RowRevision>]) -> Vec<RowPB> {
-    row_revs.iter().map(RowPB::from).collect::<Vec<_>>()
+impl GridBlockRow {
+    pub fn new(block_id: String, row_ids: Vec<String>) -> Self {
+        Self { block_id, row_ids }
+    }
 }
 
 pub(crate) fn make_row_from_row_rev(row_rev: Arc<RowRevision>) -> RowPB {
@@ -52,14 +32,3 @@ pub(crate) fn make_rows_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<RowP
 
     row_revs.iter().map(make_row).collect::<Vec<_>>()
 }
-
-pub(crate) fn make_block_pbs(blocks: Vec<GridBlock>) -> RepeatedBlockPB {
-    blocks
-        .into_iter()
-        .map(|block| {
-            let row_pbs = make_row_pb_from_row_rev(&block.row_revs);
-            BlockPB::new(&block.block_id, row_pbs)
-        })
-        .collect::<Vec<BlockPB>>()
-        .into()
-}

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

@@ -13,6 +13,8 @@ impl GridSettingChangesetBuilder {
             delete_filter: None,
             insert_group: None,
             delete_group: None,
+            alert_sort: None,
+            delete_sort: None,
         };
         Self { params }
     }

+ 58 - 0
frontend/rust-lib/flowy-grid/src/services/sort/controller.rs

@@ -0,0 +1,58 @@
+#![allow(clippy::all)]
+#[allow(unused_attributes)]
+use crate::entities::{GridSortPB, SortChangesetNotificationPB};
+use crate::services::sort::{SortChangeset, SortType};
+use flowy_task::TaskDispatcher;
+use grid_rev_model::{FieldRevision, RowRevision, SortRevision};
+use lib_infra::future::Fut;
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+pub trait SortDelegate: Send + Sync {
+    fn get_sort_rev(&self, sort_type: SortType) -> Fut<Vec<Arc<SortRevision>>>;
+    fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
+    fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
+}
+
+pub struct SortController {
+    #[allow(dead_code)]
+    view_id: String,
+    #[allow(dead_code)]
+    handler_id: String,
+    #[allow(dead_code)]
+    delegate: Box<dyn SortDelegate>,
+    task_scheduler: Arc<RwLock<TaskDispatcher>>,
+    #[allow(dead_code)]
+    sorts: Vec<GridSortPB>,
+}
+
+impl SortController {
+    pub fn new<T>(view_id: &str, handler_id: &str, delegate: T, task_scheduler: Arc<RwLock<TaskDispatcher>>) -> Self
+    where
+        T: SortDelegate + 'static,
+    {
+        Self {
+            view_id: view_id.to_string(),
+            handler_id: handler_id.to_string(),
+            delegate: Box::new(delegate),
+            task_scheduler,
+            sorts: vec![],
+        }
+    }
+
+    pub async fn close(&self) {
+        self.task_scheduler
+            .write()
+            .await
+            .unregister_handler(&self.handler_id)
+            .await;
+    }
+
+    pub fn sort_rows(&self, _rows: &mut Vec<Arc<RowRevision>>) {
+        //
+    }
+
+    pub async fn did_receive_changes(&mut self, _changeset: SortChangeset) -> Option<SortChangesetNotificationPB> {
+        None
+    }
+}

+ 67 - 0
frontend/rust-lib/flowy-grid/src/services/sort/entities.rs

@@ -0,0 +1,67 @@
+use crate::entities::{AlterSortParams, FieldType};
+use grid_rev_model::{FieldRevision, FieldTypeRevision};
+use std::sync::Arc;
+
+#[derive(Hash, Eq, PartialEq, Debug, Clone)]
+pub struct SortType {
+    pub field_id: String,
+    pub field_type: FieldType,
+}
+
+impl From<SortType> for FieldTypeRevision {
+    fn from(sort_type: SortType) -> Self {
+        sort_type.field_type.into()
+    }
+}
+
+impl std::convert::From<&AlterSortParams> for SortType {
+    fn from(params: &AlterSortParams) -> Self {
+        Self {
+            field_id: params.field_id.clone(),
+            field_type: params.field_type.into(),
+        }
+    }
+}
+
+impl std::convert::From<&Arc<FieldRevision>> for SortType {
+    fn from(rev: &Arc<FieldRevision>) -> Self {
+        Self {
+            field_id: rev.id.clone(),
+            field_type: rev.ty.into(),
+        }
+    }
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct SortChangeset {
+    pub(crate) insert_sort: Option<SortType>,
+    pub(crate) update_sort: Option<SortType>,
+    pub(crate) delete_sort: Option<SortType>,
+}
+
+impl SortChangeset {
+    pub fn from_insert(sort: SortType) -> Self {
+        Self {
+            insert_sort: Some(sort),
+            update_sort: None,
+            delete_sort: None,
+        }
+    }
+
+    pub fn from_update(sort: SortType) -> Self {
+        Self {
+            insert_sort: None,
+            update_sort: Some(sort),
+            delete_sort: None,
+        }
+    }
+
+    pub fn from_delete(sort: SortType) -> Self {
+        Self {
+            insert_sort: None,
+            update_sort: None,
+            delete_sort: Some(sort),
+        }
+    }
+}

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

@@ -0,0 +1,7 @@
+mod controller;
+mod entities;
+mod task;
+
+pub use controller::*;
+pub use entities::*;
+pub use task::*;

+ 30 - 0
frontend/rust-lib/flowy-grid/src/services/sort/task.rs

@@ -0,0 +1,30 @@
+use crate::services::sort::SortController;
+use flowy_task::{TaskContent, TaskHandler};
+use lib_infra::future::BoxResultFuture;
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+pub struct SortTaskHandler {
+    handler_id: String,
+    #[allow(dead_code)]
+    sort_controller: Arc<RwLock<SortController>>,
+}
+
+impl SortTaskHandler {
+    pub fn new(handler_id: String, sort_controller: Arc<RwLock<SortController>>) -> Self {
+        Self {
+            handler_id,
+            sort_controller,
+        }
+    }
+}
+
+impl TaskHandler for SortTaskHandler {
+    fn handler_id(&self) -> &str {
+        &self.handler_id
+    }
+
+    fn run(&self, _content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
+        todo!();
+    }
+}

+ 9 - 7
frontend/rust-lib/flowy-grid/src/services/view_editor/changed_notifier.rs

@@ -1,5 +1,5 @@
 use crate::dart_notification::{send_dart_notification, GridDartNotification};
-use crate::entities::GridBlockChangesetPB;
+use crate::entities::GridRowsVisibilityChangesetPB;
 use crate::services::filter::FilterResultNotification;
 use async_stream::stream;
 use futures::stream::StreamExt;
@@ -28,16 +28,18 @@ impl GridViewChangedReceiverRunner {
             .for_each(|changed| async {
                 match changed {
                     GridViewChanged::DidReceiveFilterResult(notification) => {
-                        let changeset = GridBlockChangesetPB {
-                            block_id: notification.block_id,
+                        let changeset = GridRowsVisibilityChangesetPB {
+                            view_id: notification.view_id,
                             visible_rows: notification.visible_rows,
                             invisible_rows: notification.invisible_rows,
-                            ..Default::default()
                         };
 
-                        send_dart_notification(&changeset.block_id, GridDartNotification::DidUpdateGridBlock)
-                            .payload(changeset)
-                            .send()
+                        send_dart_notification(
+                            &changeset.view_id,
+                            GridDartNotification::DidUpdateGridViewRowsVisibility,
+                        )
+                        .payload(changeset)
+                        .send()
                     }
                 }
             })

+ 164 - 12
frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs

@@ -1,11 +1,13 @@
 use crate::dart_notification::{send_dart_notification, GridDartNotification};
 use crate::entities::*;
+use crate::services::block_manager::GridBlockEvent;
 use crate::services::filter::{FilterChangeset, FilterController, FilterTaskHandler, FilterType, UpdatedFilterType};
 use crate::services::group::{
     default_group_configuration, find_group_field, make_group_controller, Group, GroupConfigurationReader,
     GroupController, MoveGroupRowContext,
 };
-use crate::services::row::GridBlock;
+use crate::services::row::GridBlockRowRevision;
+use crate::services::sort::{SortChangeset, SortController, SortTaskHandler, SortType};
 use crate::services::view_editor::changed_notifier::GridViewChangedNotifier;
 use crate::services::view_editor::trait_impl::*;
 use crate::services::view_editor::GridViewChangedReceiverRunner;
@@ -16,12 +18,14 @@ use flowy_revision::RevisionManager;
 use flowy_sync::client_grid::{make_grid_view_operations, GridViewRevisionChangeset, GridViewRevisionPad};
 use flowy_task::TaskDispatcher;
 use grid_rev_model::{
-    gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterRevision, LayoutRevision, RowChangeset, RowRevision,
+    gen_grid_filter_id, gen_grid_sort_id, FieldRevision, FieldTypeRevision, FilterRevision, LayoutRevision,
+    RowChangeset, RowRevision, SortRevision,
 };
 use lib_infra::async_trait::async_trait;
 use lib_infra::future::Fut;
 use lib_infra::ref_map::RefCountValue;
 use nanoid::nanoid;
+use std::borrow::Cow;
 use std::future::Future;
 use std::sync::Arc;
 use tokio::sync::{broadcast, RwLock};
@@ -29,13 +33,29 @@ use tokio::sync::{broadcast, RwLock};
 pub trait GridViewEditorDelegate: Send + Sync + 'static {
     /// If the field_ids is None, then it will return all the field revisions
     fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
+
+    /// Returns the field with the field_id
     fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
 
+    /// Returns the index of the row with row_id
     fn index_of_row(&self, row_id: &str) -> Fut<Option<usize>>;
+
+    /// Returns the `index` and `RowRevision` with row_id
     fn get_row_rev(&self, row_id: &str) -> Fut<Option<(usize, Arc<RowRevision>)>>;
-    fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>>;
-    fn get_blocks(&self) -> Fut<Vec<GridBlock>>;
 
+    /// Returns all the rows that the block has. If the passed-in block_ids is None, then will return all the rows
+    /// The relationship between the grid and the block is:
+    ///     A grid has a list of blocks
+    ///     A block has a list of rows
+    ///     A row has a list of cells
+    ///
+    fn get_row_revs(&self, block_ids: Option<Vec<String>>) -> Fut<Vec<Arc<RowRevision>>>;
+
+    /// Get all the blocks that the current Grid has.
+    /// One grid has a list of blocks
+    fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>>;
+
+    /// Returns a `TaskDispatcher` used to poll a `Task`
     fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
 }
 
@@ -47,6 +67,7 @@ pub struct GridViewRevisionEditor {
     delegate: Arc<dyn GridViewEditorDelegate>,
     group_controller: Arc<RwLock<Box<dyn GroupController>>>,
     filter_controller: Arc<RwLock<FilterController>>,
+    sort_controller: Arc<RwLock<SortController>>,
     pub notifier: GridViewChangedNotifier,
 }
 
@@ -89,6 +110,8 @@ impl GridViewRevisionEditor {
         )
         .await?;
 
+        let sort_controller = make_sort_controller(&view_id, delegate.clone(), view_rev_pad.clone()).await;
+
         let user_id = user_id.to_owned();
         let group_controller = Arc::new(RwLock::new(group_controller));
         let filter_controller =
@@ -101,6 +124,7 @@ impl GridViewRevisionEditor {
             delegate,
             group_controller,
             filter_controller,
+            sort_controller,
             notifier,
         })
     }
@@ -110,6 +134,40 @@ impl GridViewRevisionEditor {
         self.rev_manager.generate_snapshot().await;
         self.rev_manager.close().await;
         self.filter_controller.read().await.close().await;
+        self.sort_controller.read().await.close().await;
+    }
+
+    pub async fn handle_block_event(&self, event: Cow<'_, GridBlockEvent>) {
+        let changeset = match event.into_owned() {
+            GridBlockEvent::InsertRow { block_id: _, row } => {
+                //
+                GridViewRowsChangesetPB::from_insert(self.view_id.clone(), vec![row])
+            }
+            GridBlockEvent::UpdateRow { block_id: _, row } => {
+                //
+                GridViewRowsChangesetPB::from_update(self.view_id.clone(), vec![row])
+            }
+            GridBlockEvent::DeleteRow { block_id: _, row_id } => {
+                //
+                GridViewRowsChangesetPB::from_delete(self.view_id.clone(), vec![row_id])
+            }
+            GridBlockEvent::Move {
+                block_id: _,
+                deleted_row_id,
+                inserted_row,
+            } => {
+                //
+                GridViewRowsChangesetPB::from_move(self.view_id.clone(), vec![deleted_row_id], vec![inserted_row])
+            }
+        };
+
+        send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGridViewRows)
+            .payload(changeset)
+            .send();
+    }
+
+    pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
+        self.sort_controller.read().await.sort_rows(rows)
     }
 
     pub async fn filter_rows(&self, _block_id: &str, mut rows: Vec<Arc<RowRevision>>) -> Vec<Arc<RowRevision>> {
@@ -316,6 +374,70 @@ impl GridViewRevisionEditor {
         .await
     }
 
+    pub async fn insert_view_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
+        let sort_type = SortType::from(&params);
+        let is_exist = params.sort_id.is_some();
+        let sort_id = match params.sort_id {
+            None => gen_grid_sort_id(),
+            Some(sort_id) => sort_id,
+        };
+
+        let sort_rev = SortRevision {
+            id: sort_id,
+            field_id: params.field_id.clone(),
+            field_type: params.field_type,
+            condition: params.condition,
+        };
+
+        let mut sort_controller = self.sort_controller.write().await;
+        let changeset = if is_exist {
+            self.modify(|pad| {
+                let changeset = pad.update_sort(&params.field_id, sort_rev)?;
+                Ok(changeset)
+            })
+            .await?;
+            sort_controller
+                .did_receive_changes(SortChangeset::from_update(sort_type))
+                .await
+        } else {
+            self.modify(|pad| {
+                let changeset = pad.insert_sort(&params.field_id, sort_rev)?;
+                Ok(changeset)
+            })
+            .await?;
+            sort_controller
+                .did_receive_changes(SortChangeset::from_insert(sort_type))
+                .await
+        };
+
+        if let Some(changeset) = changeset {
+            self.notify_did_update_sort(changeset).await;
+        }
+        Ok(())
+    }
+
+    pub async fn delete_view_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
+        let sort_type = params.sort_type;
+        let changeset = self
+            .sort_controller
+            .write()
+            .await
+            .did_receive_changes(SortChangeset::from_delete(sort_type.clone()))
+            .await;
+
+        let _ = self
+            .modify(|pad| {
+                let changeset = pad.delete_sort(&params.sort_id, &sort_type.field_id, sort_type.field_type)?;
+                Ok(changeset)
+            })
+            .await?;
+
+        if changeset.is_some() {
+            self.notify_did_update_sort(changeset.unwrap()).await;
+        }
+        Ok(())
+    }
+
     #[tracing::instrument(level = "trace", skip(self), err)]
     pub async fn insert_view_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
         let filter_type = FilterType::from(&params);
@@ -344,7 +466,7 @@ impl GridViewRevisionEditor {
             })
             .await?;
             filter_controller
-                .did_receive_filter_changed(FilterChangeset::from_update(UpdatedFilterType::new(
+                .did_receive_changes(FilterChangeset::from_update(UpdatedFilterType::new(
                     old_filter_type,
                     filter_type,
                 )))
@@ -356,7 +478,7 @@ impl GridViewRevisionEditor {
             })
             .await?;
             filter_controller
-                .did_receive_filter_changed(FilterChangeset::from_insert(filter_type))
+                .did_receive_changes(FilterChangeset::from_insert(filter_type))
                 .await
         };
 
@@ -369,17 +491,16 @@ impl GridViewRevisionEditor {
     #[tracing::instrument(level = "trace", skip(self), err)]
     pub async fn delete_view_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
         let filter_type = params.filter_type;
-        let field_type_rev = filter_type.field_type_rev();
         let changeset = self
             .filter_controller
             .write()
             .await
-            .did_receive_filter_changed(FilterChangeset::from_delete(filter_type.clone()))
+            .did_receive_changes(FilterChangeset::from_delete(filter_type.clone()))
             .await;
 
         let _ = self
             .modify(|pad| {
-                let changeset = pad.delete_filter(&params.filter_id, &filter_type.field_id, &field_type_rev)?;
+                let changeset = pad.delete_filter(&params.filter_id, &filter_type.field_id, filter_type.field_type)?;
                 Ok(changeset)
             })
             .await?;
@@ -405,7 +526,7 @@ impl GridViewRevisionEditor {
                 .filter_controller
                 .write()
                 .await
-                .did_receive_filter_changed(filter_changeset)
+                .did_receive_changes(filter_changeset)
                 .await
             {
                 self.notify_did_update_filter(changeset).await;
@@ -423,7 +544,7 @@ impl GridViewRevisionEditor {
     #[tracing::instrument(level = "debug", skip_all, err)]
     pub async fn group_by_view_field(&self, field_id: &str) -> FlowyResult<()> {
         if let Some(field_rev) = self.delegate.get_field_rev(field_id).await {
-            let row_revs = self.delegate.get_row_revs().await;
+            let row_revs = self.delegate.get_row_revs(None).await;
             let new_group_controller = new_group_controller_with_field_rev(
                 self.user_id.clone(),
                 self.view_id.clone(),
@@ -476,6 +597,12 @@ impl GridViewRevisionEditor {
             .send();
     }
 
+    pub async fn notify_did_update_sort(&self, changeset: SortChangesetNotificationPB) {
+        send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
+            .payload(changeset)
+            .send();
+    }
+
     async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
         send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGroupView)
             .payload(changeset)
@@ -543,7 +670,7 @@ async fn new_group_controller(
 ) -> FlowyResult<Box<dyn GroupController>> {
     let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
     let field_revs = delegate.get_field_revs(None).await;
-    let row_revs = delegate.get_row_revs().await;
+    let row_revs = delegate.get_row_revs(None).await;
     let layout = view_rev_pad.read().await.layout();
     // Read the group field or find a new group field
     let field_rev = configuration_reader
@@ -610,6 +737,31 @@ async fn make_filter_controller(
     filter_controller
 }
 
+async fn make_sort_controller(
+    view_id: &str,
+    delegate: Arc<dyn GridViewEditorDelegate>,
+    pad: Arc<RwLock<GridViewRevisionPad>>,
+) -> Arc<RwLock<SortController>> {
+    let handler_id = gen_handler_id();
+    let sort_delegate = GridViewSortDelegateImpl {
+        editor_delegate: delegate.clone(),
+        view_revision_pad: pad,
+    };
+    let task_scheduler = delegate.get_task_scheduler();
+    let sort_controller = Arc::new(RwLock::new(SortController::new(
+        view_id,
+        &handler_id,
+        sort_delegate,
+        task_scheduler.clone(),
+    )));
+    task_scheduler
+        .write()
+        .await
+        .register_handler(SortTaskHandler::new(handler_id, sort_controller.clone()));
+
+    sort_controller
+}
+
 fn gen_handler_id() -> String {
     nanoid!(10)
 }

+ 59 - 9
frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs

@@ -1,8 +1,9 @@
 use crate::entities::{
-    AlterFilterParams, CreateRowParams, DeleteFilterParams, DeleteGroupParams, GridSettingPB, InsertGroupParams,
-    MoveGroupParams, RepeatedGroupPB, RowPB,
+    AlterFilterParams, AlterSortParams, CreateRowParams, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
+    GridSettingPB, InsertGroupParams, MoveGroupParams, RepeatedGroupPB, RowPB,
 };
 use crate::manager::GridUser;
+use crate::services::block_manager::GridBlockEvent;
 use crate::services::filter::FilterType;
 use crate::services::persistence::rev_sqlite::{
     SQLiteGridRevisionSnapshotPersistence, SQLiteGridViewRevisionPersistence,
@@ -16,6 +17,7 @@ use flowy_revision::{RevisionManager, RevisionPersistence, RevisionPersistenceCo
 use grid_rev_model::{FieldRevision, FilterRevision, RowChangeset, RowRevision};
 use lib_infra::future::Fut;
 use lib_infra::ref_map::RefCountHashMap;
+use std::borrow::Cow;
 use std::sync::Arc;
 use tokio::sync::{broadcast, RwLock};
 
@@ -23,7 +25,7 @@ pub struct GridViewManager {
     grid_id: String,
     user: Arc<dyn GridUser>,
     delegate: Arc<dyn GridViewEditorDelegate>,
-    view_editors: RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>,
+    view_editors: Arc<RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>>,
 }
 
 impl GridViewManager {
@@ -31,8 +33,10 @@ impl GridViewManager {
         grid_id: String,
         user: Arc<dyn GridUser>,
         delegate: Arc<dyn GridViewEditorDelegate>,
+        block_event_rx: broadcast::Receiver<GridBlockEvent>,
     ) -> FlowyResult<Self> {
-        let view_editors = RwLock::new(RefCountHashMap::default());
+        let view_editors = Arc::new(RwLock::new(RefCountHashMap::default()));
+        listen_on_grid_block_event(block_event_rx, view_editors.clone());
         Ok(Self {
             grid_id,
             user,
@@ -49,9 +53,24 @@ impl GridViewManager {
         Ok(self.get_view_editor(view_id).await?.notifier.subscribe())
     }
 
-    pub async fn filter_rows(&self, block_id: &str, rows: Vec<Arc<RowRevision>>) -> FlowyResult<Vec<Arc<RowRevision>>> {
-        let editor = self.get_default_view_editor().await?;
-        let rows = editor.filter_rows(block_id, rows).await;
+    pub async fn get_row_revs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
+        let mut row_revs = self.delegate.get_row_revs(Some(vec![block_id.to_owned()])).await;
+        if let Ok(view_editor) = self.get_view_editor(view_id).await {
+            view_editor.sort_rows(&mut row_revs).await;
+        }
+        Ok(row_revs)
+    }
+
+    pub async fn filter_rows(
+        &self,
+        view_id: &str,
+        block_id: &str,
+        rows: Vec<Arc<RowRevision>>,
+    ) -> FlowyResult<Vec<Arc<RowRevision>>> {
+        let rows = match self.get_view_editor(view_id).await {
+            Ok(view_editor) => view_editor.filter_rows(block_id, rows).await,
+            Err(_) => rows,
+        };
         Ok(rows)
     }
 
@@ -117,15 +136,25 @@ impl GridViewManager {
     }
 
     pub async fn create_or_update_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
-        let view_editor = self.get_default_view_editor().await?;
+        let view_editor = self.get_view_editor(&params.view_id).await?;
         view_editor.insert_view_filter(params).await
     }
 
     pub async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
-        let view_editor = self.get_default_view_editor().await?;
+        let view_editor = self.get_view_editor(&params.view_id).await?;
         view_editor.delete_view_filter(params).await
     }
 
+    pub async fn create_or_update_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
+        let view_editor = self.get_view_editor(&params.view_id).await?;
+        view_editor.insert_view_sort(params).await
+    }
+
+    pub async fn delete_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
+        let view_editor = self.get_view_editor(&params.view_id).await?;
+        view_editor.delete_view_sort(params).await
+    }
+
     pub async fn load_groups(&self) -> FlowyResult<RepeatedGroupPB> {
         let view_editor = self.get_default_view_editor().await?;
         let groups = view_editor.load_view_groups().await?;
@@ -225,6 +254,27 @@ impl GridViewManager {
     }
 }
 
+fn listen_on_grid_block_event(
+    mut block_event_rx: broadcast::Receiver<GridBlockEvent>,
+    view_editors: Arc<RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>>,
+) {
+    tokio::spawn(async move {
+        loop {
+            while let Ok(event) = block_event_rx.recv().await {
+                let read_guard = view_editors.read().await;
+                let view_editors = read_guard.values();
+                let event = if view_editors.len() == 1 {
+                    Cow::Owned(event)
+                } else {
+                    Cow::Borrowed(&event)
+                };
+                for view_editor in view_editors.iter() {
+                    view_editor.handle_block_event(event.clone()).await;
+                }
+            }
+        }
+    });
+}
 pub async fn make_grid_view_rev_manager(
     user: &Arc<dyn GridUser>,
     view_id: &str,

+ 32 - 6
frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs

@@ -1,7 +1,8 @@
 use crate::entities::{GridLayout, GridLayoutPB, GridSettingPB};
 use crate::services::filter::{FilterDelegate, FilterType};
 use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter};
-use crate::services::row::GridBlock;
+use crate::services::row::GridBlockRowRevision;
+use crate::services::sort::{SortDelegate, SortType};
 use crate::services::view_editor::GridViewEditorDelegate;
 use bytes::Bytes;
 use flowy_database::ConnectionPool;
@@ -12,7 +13,9 @@ use flowy_revision::{
 };
 use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
 use flowy_sync::util::make_operations_from_revisions;
-use grid_rev_model::{FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, RowRevision};
+use grid_rev_model::{
+    FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, RowRevision, SortRevision,
+};
 use lib_infra::future::{to_fut, Fut, FutureResult};
 use lib_ot::core::EmptyAttributes;
 use std::sync::Arc;
@@ -132,11 +135,11 @@ pub(crate) struct GridViewFilterDelegateImpl {
 }
 
 impl FilterDelegate for GridViewFilterDelegateImpl {
-    fn get_filter_rev(&self, filter_id: FilterType) -> Fut<Option<Arc<FilterRevision>>> {
+    fn get_filter_rev(&self, filter_type: FilterType) -> Fut<Option<Arc<FilterRevision>>> {
         let pad = self.view_revision_pad.clone();
         to_fut(async move {
-            let field_type_rev: FieldTypeRevision = filter_id.field_type.into();
-            let mut filters = pad.read().await.get_filters(&filter_id.field_id, &field_type_rev);
+            let field_type_rev: FieldTypeRevision = filter_type.field_type.into();
+            let mut filters = pad.read().await.get_filters(&filter_type.field_id, &field_type_rev);
             if filters.is_empty() {
                 None
             } else {
@@ -154,7 +157,7 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
         self.editor_delegate.get_field_revs(field_ids)
     }
 
-    fn get_blocks(&self) -> Fut<Vec<GridBlock>> {
+    fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>> {
         self.editor_delegate.get_blocks()
     }
 
@@ -162,3 +165,26 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
         self.editor_delegate.get_row_rev(row_id)
     }
 }
+
+pub(crate) struct GridViewSortDelegateImpl {
+    pub(crate) editor_delegate: Arc<dyn GridViewEditorDelegate>,
+    pub(crate) view_revision_pad: Arc<RwLock<GridViewRevisionPad>>,
+}
+
+impl SortDelegate for GridViewSortDelegateImpl {
+    fn get_sort_rev(&self, sort_type: SortType) -> Fut<Vec<Arc<SortRevision>>> {
+        let pad = self.view_revision_pad.clone();
+        to_fut(async move {
+            let field_type_rev: FieldTypeRevision = sort_type.field_type.into();
+            pad.read().await.get_sorts(&sort_type.field_id, &field_type_rev)
+        })
+    }
+
+    fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>> {
+        self.editor_delegate.get_field_rev(field_id)
+    }
+
+    fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>> {
+        self.editor_delegate.get_field_revs(field_ids)
+    }
+}

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

@@ -1,9 +1,9 @@
 use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
 use crate::grid::block_test::util::GridRowTestBuilder;
 use crate::grid::grid_editor::GridEditorTest;
-
 use flowy_grid::entities::{CellPathParams, CreateRowParams, FieldType, GridLayout, RowPB};
 use flowy_grid::services::field::*;
+use flowy_grid::services::row::GridBlockRow;
 use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision};
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -82,28 +82,27 @@ impl GridRowTest {
                     layout: GridLayout::Table,
                 };
                 let row_order = self.editor.create_row(params).await.unwrap();
-                self.row_order_by_row_id
-                    .insert(row_order.row_id().to_owned(), row_order);
+                self.row_by_row_id.insert(row_order.row_id().to_owned(), row_order);
                 self.row_revs = self.get_row_revs().await;
                 self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             RowScript::CreateRow { row_rev } => {
                 let row_orders = self.editor.insert_rows(vec![row_rev]).await.unwrap();
                 for row_order in row_orders {
-                    self.row_order_by_row_id
-                        .insert(row_order.row_id().to_owned(), row_order);
+                    self.row_by_row_id.insert(row_order.row_id().to_owned(), row_order);
                 }
                 self.row_revs = self.get_row_revs().await;
                 self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             RowScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
             RowScript::DeleteRows { row_ids } => {
-                let row_orders = row_ids
+                let row_pbs = row_ids
                     .into_iter()
-                    .map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
+                    .map(|row_id| self.row_by_row_id.get(&row_id).unwrap().clone())
                     .collect::<Vec<RowPB>>();
 
-                self.editor.delete_rows(row_orders).await.unwrap();
+                let block_rows = block_from_row_pbs(row_pbs);
+                self.editor.delete_rows(block_rows).await.unwrap();
                 self.row_revs = self.get_row_revs().await;
                 self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
@@ -270,6 +269,19 @@ impl GridRowTest {
     }
 }
 
+fn block_from_row_pbs(row_orders: Vec<RowPB>) -> Vec<GridBlockRow> {
+    let mut map: HashMap<String, GridBlockRow> = HashMap::new();
+    row_orders.into_iter().for_each(|row_pb| {
+        let block_id = row_pb.block_id().to_owned();
+        let cloned_block_id = block_id.clone();
+        map.entry(block_id)
+            .or_insert_with(|| GridBlockRow::new(cloned_block_id, vec![]))
+            .row_ids
+            .push(row_pb.id);
+    });
+    map.into_values().collect::<Vec<_>>()
+}
+
 impl std::ops::Deref for GridRowTest {
     type Target = GridEditorTest;
 

+ 3 - 3
frontend/rust-lib/flowy-grid/tests/grid/filter_test/checkbox_filter_test.rs

@@ -1,6 +1,6 @@
 use crate::grid::filter_test::script::FilterScript::*;
 use crate::grid::filter_test::script::GridFilterTest;
-use flowy_grid::entities::CheckboxFilterCondition;
+use flowy_grid::entities::CheckboxFilterConditionPB;
 
 #[tokio::test]
 async fn grid_filter_checkbox_is_check_test() {
@@ -9,7 +9,7 @@ async fn grid_filter_checkbox_is_check_test() {
     // The initial number of checked is 2
     let scripts = vec![
         CreateCheckboxFilter {
-            condition: CheckboxFilterCondition::IsChecked,
+            condition: CheckboxFilterConditionPB::IsChecked,
         },
         AssertFilterChanged {
             visible_row_len: 0,
@@ -24,7 +24,7 @@ async fn grid_filter_checkbox_is_uncheck_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateCheckboxFilter {
-            condition: CheckboxFilterCondition::IsUnChecked,
+            condition: CheckboxFilterConditionPB::IsUnChecked,
         },
         AssertNumberOfVisibleRows { expected: 3 },
     ];

+ 3 - 3
frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs

@@ -1,13 +1,13 @@
 use crate::grid::filter_test::script::FilterScript::*;
 use crate::grid::filter_test::script::GridFilterTest;
-use flowy_grid::entities::ChecklistFilterCondition;
+use flowy_grid::entities::ChecklistFilterConditionPB;
 
 #[tokio::test]
 async fn grid_filter_checklist_is_incomplete_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateChecklistFilter {
-            condition: ChecklistFilterCondition::IsIncomplete,
+            condition: ChecklistFilterConditionPB::IsIncomplete,
         },
         AssertNumberOfVisibleRows { expected: 4 },
     ];
@@ -19,7 +19,7 @@ async fn grid_filter_checklist_is_complete_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateChecklistFilter {
-            condition: ChecklistFilterCondition::IsComplete,
+            condition: ChecklistFilterConditionPB::IsComplete,
         },
         AssertNumberOfVisibleRows { expected: 1 },
     ];

+ 6 - 6
frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs

@@ -1,13 +1,13 @@
 use crate::grid::filter_test::script::FilterScript::*;
 use crate::grid::filter_test::script::GridFilterTest;
-use flowy_grid::entities::DateFilterCondition;
+use flowy_grid::entities::DateFilterConditionPB;
 
 #[tokio::test]
 async fn grid_filter_date_is_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
-            condition: DateFilterCondition::DateIs,
+            condition: DateFilterConditionPB::DateIs,
             start: None,
             end: None,
             timestamp: Some(1647251762),
@@ -22,7 +22,7 @@ async fn grid_filter_date_after_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
-            condition: DateFilterCondition::DateAfter,
+            condition: DateFilterConditionPB::DateAfter,
             start: None,
             end: None,
             timestamp: Some(1647251762),
@@ -37,7 +37,7 @@ async fn grid_filter_date_on_or_after_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
-            condition: DateFilterCondition::DateOnOrAfter,
+            condition: DateFilterConditionPB::DateOnOrAfter,
             start: None,
             end: None,
             timestamp: Some(1668359085),
@@ -52,7 +52,7 @@ async fn grid_filter_date_on_or_before_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
-            condition: DateFilterCondition::DateOnOrBefore,
+            condition: DateFilterConditionPB::DateOnOrBefore,
             start: None,
             end: None,
             timestamp: Some(1668359085),
@@ -67,7 +67,7 @@ async fn grid_filter_date_within_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
-            condition: DateFilterCondition::DateWithIn,
+            condition: DateFilterConditionPB::DateWithIn,
             start: Some(1647251762),
             end: Some(1668704685),
             timestamp: None,

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio