Преглед изворни кода

chore: hide the grid blocks from Dart

nathan пре 2 година
родитељ
комит
0cf0b29721
39 измењених фајлова са 394 додато и 643 уклоњено
  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 1
      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 3
      frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
  8. 15 49
      frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart
  9. 0 1
      frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
  10. 5 13
      frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart
  11. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_list.dart
  12. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_listener.dart
  13. 0 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart
  14. 4 7
      frontend/app_flowy/lib/plugins/grid/application/view/grid_view_cache.dart
  15. 2 11
      frontend/app_flowy/test/bloc_test/board_test/util.dart
  16. 2 9
      frontend/app_flowy/test/bloc_test/grid_test/util.dart
  17. 0 1
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  18. 0 245
      frontend/rust-lib/flowy-grid/src/entities/block_entities.rs
  19. 2 2
      frontend/rust-lib/flowy-grid/src/entities/grid_entities.rs
  20. 0 2
      frontend/rust-lib/flowy-grid/src/entities/mod.rs
  21. 130 0
      frontend/rust-lib/flowy-grid/src/entities/row_entities.rs
  22. 15 6
      frontend/rust-lib/flowy-grid/src/entities/view_entities.rs
  23. 1 12
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  24. 1 8
      frontend/rust-lib/flowy-grid/src/event_map.rs
  25. 4 0
      frontend/rust-lib/flowy-grid/src/services/block_editor.rs
  26. 68 77
      frontend/rust-lib/flowy-grid/src/services/block_manager.rs
  27. 2 2
      frontend/rust-lib/flowy-grid/src/services/filter/controller.rs
  28. 9 13
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  29. 2 2
      frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs
  30. 1 1
      frontend/rust-lib/flowy-grid/src/services/row/mod.rs
  31. 10 41
      frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs
  32. 0 3
      frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
  33. 1 1
      frontend/rust-lib/flowy-grid/src/services/view_editor/changed_notifier.rs
  34. 33 2
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs
  35. 25 4
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs
  36. 2 2
      frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs
  37. 20 8
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  38. 1 3
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  39. 2 2
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.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 - 1
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';

+ 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.DidUpdateGridRows:
-        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 - 3
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';
@@ -69,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';

+ 5 - 13
frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart

@@ -3,9 +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/protobuf.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
@@ -28,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.
@@ -47,7 +45,7 @@ class GridRowCache {
 
   GridRowCache({
     required this.gridId,
-    required this.block,
+    required this.rows,
     required IGridRowFieldNotifier notifier,
   })  : _cellCache = GridCellCache(gridId: gridId),
         _rowChangeReasonNotifier = _RowChangesetNotifier(),
@@ -57,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);
     }
@@ -70,14 +70,6 @@ class GridRowCache {
     await _cellCache.dispose();
   }
 
-  void applyChangesets(GridBlockChangesetPB changeset) {
-    _deleteRows(changeset.deletedRows);
-    _insertRows(changeset.insertedRows);
-    _updateRows(changeset.updatedRows);
-    _hideRows(changeset.invisibleRows);
-    _showRows(changeset.visibleRows);
-  }
-
   void applyRowsChanged(GridViewRowsChangesetPB changeset) {
     _deleteRows(changeset.deletedRows);
     _insertRows(changeset.insertedRows);

+ 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 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_service.dart

@@ -1,7 +1,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-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';

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

@@ -1,29 +1,26 @@
 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';
 
 /// 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;
   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),
     );
 

+ 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(

+ 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(

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

@@ -6,7 +6,6 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
 pub enum GridDartNotification {
     Unknown = 0,
     DidCreateBlock = 11,
-    DidUpdateGridRows = 19,
     DidUpdateGridViewRows = 20,
     DidUpdateGridViewRowsVisibility = 21,
     DidUpdateGridFields = 22,

+ 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 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>,
-
-    #[pb(index = 5)]
-    pub visible_rows: Vec<InsertedRowPB>,
-
-    #[pb(index = 6)]
-    pub invisible_rows: Vec<String>,
-}
-impl GridBlockChangesetPB {
-    pub fn insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
-        Self {
-            view_id,
-            inserted_rows,
-            ..Default::default()
-        }
-    }
-
-    pub fn delete(block_id: &str, deleted_rows: Vec<String>) -> Self {
-        Self {
-            view_id: block_id.to_owned(),
-            deleted_rows,
-            ..Default::default()
-        }
-    }
-
-    pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowPB>) -> Self {
-        Self {
-            view_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,
-        })
-    }
-}

+ 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)]

+ 0 - 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;
@@ -9,7 +8,6 @@ mod row_entities;
 pub mod setting_entities;
 mod view_entities;
 
-pub use block_entities::*;
 pub use cell_entities::*;
 pub use field_entities::*;
 pub use filter_entities::*;

+ 130 - 0
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 {

+ 15 - 6
frontend/rust-lib/flowy-grid/src/entities/view_entities.rs

@@ -1,4 +1,4 @@
-use crate::entities::{GridLayout, InsertedRowPB, UpdatedRowPB};
+use crate::entities::{InsertedRowPB, UpdatedRowPB};
 use flowy_derive::ProtoBuf;
 
 #[derive(Debug, Default, Clone, ProtoBuf)]
@@ -29,7 +29,7 @@ pub struct GridViewRowsChangesetPB {
 }
 
 impl GridViewRowsChangesetPB {
-    pub fn insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
+    pub fn from_insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
         Self {
             view_id,
             inserted_rows,
@@ -37,19 +37,28 @@ impl GridViewRowsChangesetPB {
         }
     }
 
-    pub fn delete(block_id: &str, deleted_rows: Vec<String>) -> Self {
+    pub fn from_delete(view_id: String, deleted_rows: Vec<String>) -> Self {
         Self {
-            view_id: block_id.to_owned(),
+            view_id,
             deleted_rows,
             ..Default::default()
         }
     }
 
-    pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowPB>) -> Self {
+    pub fn from_update(view_id: String, updated_rows: Vec<UpdatedRowPB>) -> Self {
         Self {
-            view_id: block_id.to_owned(),
+            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()
+        }
+    }
 }

+ 1 - 12
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};
@@ -74,17 +74,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>,

+ 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 - 0
frontend/rust-lib/flowy-grid/src/services/block_editor.rs

@@ -113,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)

+ 68 - 77
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;
@@ -14,20 +14,28 @@ use flowy_revision::{RevisionManager, RevisionPersistence, RevisionPersistenceCo
 use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision};
 use std::borrow::Cow;
 use std::collections::HashMap;
-use std::sync::{Arc, Weak};
-use tokio::sync::{broadcast, RwLock};
-
-pub trait GridBlockDelegate: Send + Sync + 'static {
-    fn notify_did_insert_row(&self, row: InsertedRowPB);
-    fn notify_did_update_row(&self, row: UpdatedRowPB);
-    fn notify_did_delete_row(&self, row_id: String);
-}
+use std::sync::Arc;
+use tokio::sync::broadcast;
 
 #[derive(Debug, Clone)]
 pub enum GridBlockEvent {
-    DidInsertRow { row: InsertedRowPB },
-    UpdateRow { row: UpdatedRowPB },
-    DeleteRow { row_id: String },
+    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;
@@ -35,7 +43,7 @@ pub(crate) struct GridBlockManager {
     user: Arc<dyn GridUser>,
     persistence: Arc<BlockIndexCache>,
     block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
-    notifier: broadcast::Sender<GridBlockEvent>,
+    event_notifier: broadcast::Sender<GridBlockEvent>,
 }
 
 impl GridBlockManager {
@@ -43,7 +51,7 @@ impl GridBlockManager {
         user: &Arc<dyn GridUser>,
         block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
         persistence: Arc<BlockIndexCache>,
-        block_event_tx: broadcast::Sender<GridBlockEvent>,
+        event_notifier: broadcast::Sender<GridBlockEvent>,
     ) -> FlowyResult<Self> {
         let block_editors = make_block_editors(user, block_meta_revs).await?;
         let user = user.clone();
@@ -51,7 +59,7 @@ impl GridBlockManager {
             user,
             block_editors,
             persistence,
-            notifier: block_event_tx,
+            event_notifier,
         };
         Ok(manager)
     }
@@ -87,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(
@@ -101,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)
@@ -135,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(())
@@ -157,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);
         }
 
@@ -196,16 +199,11 @@ impl GridBlockManager {
             is_new: false,
         };
 
-        let notified_changeset = GridBlockChangesetPB {
-            view_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?;
+        let _ = self.event_notifier.send(GridBlockEvent::Move {
+            block_id: editor.block_id.clone(),
+            deleted_row_id: delete_row_id,
+            inserted_row: insert_row,
+        });
 
         Ok(())
     }
@@ -235,7 +233,7 @@ impl GridBlockManager {
         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 => {
@@ -243,27 +241,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::DidUpdateGridRows)
-            .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();

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

@@ -3,7 +3,7 @@ use crate::entities::{FieldType, InsertedRowPB, RowPB};
 use crate::services::cell::{CellFilterOperation, 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>)>>;
 }
 

+ 9 - 13
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -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;
@@ -511,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
@@ -527,8 +527,8 @@ 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?;
         }
@@ -538,21 +538,17 @@ impl GridRevisionEditor {
     pub async fn get_grid(&self) -> 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(&block_rev.block_id).await {
+                all_rows.extend(rows);
+            }
         }
 
         Ok(GridPB {
             id: self.grid_id.clone(),
             fields,
-            blocks,
+            rows: all_rows,
         })
     }
 

+ 2 - 2
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;
@@ -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() })
     }

+ 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()
-}

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

@@ -1,6 +1,3 @@
-use crate::services::block_manager::GridBlockManager;
-use std::sync::Arc;
-
 pub struct SortController {}
 impl SortController {
     pub fn new() -> Self {

+ 1 - 1
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, GridRowsVisibilityChangesetPB};
+use crate::entities::GridRowsVisibilityChangesetPB;
 use crate::services::filter::FilterResultNotification;
 use async_stream::stream;
 use futures::stream::StreamExt;

+ 33 - 2
frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs

@@ -1,11 +1,12 @@
 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::SortController;
 use crate::services::view_editor::changed_notifier::GridViewChangedNotifier;
 use crate::services::view_editor::trait_impl::*;
@@ -23,6 +24,7 @@ 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};
@@ -46,7 +48,7 @@ pub trait GridViewEditorDelegate: Send + Sync + 'static {
 
     /// Get all the blocks that the current Grid has.
     /// One grid has a list of blocks
-    fn get_blocks(&self) -> Fut<Vec<GridBlock>>;
+    fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>>;
 
     fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
 }
@@ -128,6 +130,35 @@ impl GridViewRevisionEditor {
         self.filter_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 notify_rows_did_changed(&self) {
         //
     }

+ 25 - 4
frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs

@@ -17,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};
 
@@ -24,8 +25,7 @@ pub struct GridViewManager {
     grid_id: String,
     user: Arc<dyn GridUser>,
     delegate: Arc<dyn GridViewEditorDelegate>,
-    view_editors: RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>,
-    block_event_rx: broadcast::Receiver<GridBlockEvent>,
+    view_editors: Arc<RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>>,
 }
 
 impl GridViewManager {
@@ -35,13 +35,13 @@ impl GridViewManager {
         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,
             delegate,
             view_editors,
-            block_event_rx,
         })
     }
 
@@ -233,6 +233,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,

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

@@ -1,7 +1,7 @@
 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::view_editor::GridViewEditorDelegate;
 use bytes::Bytes;
 use flowy_database::ConnectionPool;
@@ -154,7 +154,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()
     }
 

+ 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;
 

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

@@ -226,10 +226,8 @@ impl GridFilterTest {
                 }
             }
             FilterScript::AssertNumberOfVisibleRows { expected } => {
-                //
                 let grid = self.editor.get_grid().await.unwrap();
-                let rows = grid.blocks.into_iter().map(|block| block.rows).flatten().collect::<Vec<RowPB>>();
-                assert_eq!(rows.len(), expected);
+                assert_eq!(grid.rows.len(), expected);
             }
             FilterScript::Wait { millisecond } => {
                 tokio::time::sleep(Duration::from_millis(millisecond)).await;

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

@@ -30,7 +30,7 @@ pub struct GridEditorTest {
     pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
     pub row_revs: Vec<Arc<RowRevision>>,
     pub field_count: usize,
-    pub row_order_by_row_id: HashMap<String, RowPB>,
+    pub row_by_row_id: HashMap<String, RowPB>,
 }
 
 impl GridEditorTest {
@@ -77,7 +77,7 @@ impl GridEditorTest {
             block_meta_revs,
             row_revs,
             field_count: FieldType::COUNT,
-            row_order_by_row_id: HashMap::default(),
+            row_by_row_id: HashMap::default(),
         }
     }