Browse Source

chore: render rows

appflowy 3 years ago
parent
commit
05bcd38534
61 changed files with 1885 additions and 779 deletions
  1. 8 10
      frontend/app_flowy/lib/startup/home_deps_resolver.dart
  2. 2 11
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/cell_service.dart
  3. 0 43
      frontend/app_flowy/lib/workspace/application/grid/data.dart
  4. 54 48
      frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart
  5. 49 5
      frontend/app_flowy/lib/workspace/application/grid/grid_block_service.dart
  6. 4 0
      frontend/app_flowy/lib/workspace/application/grid/grid_listenr.dart
  7. 20 4
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  8. 33 6
      frontend/app_flowy/lib/workspace/application/grid/row_bloc.dart
  9. 26 2
      frontend/app_flowy/lib/workspace/application/grid/row_service.dart
  10. 15 19
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart
  11. 9 8
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_builder.dart
  12. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/checkbox_cell.dart
  13. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/date_cell.dart
  14. 10 19
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart
  15. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/number_cell.dart
  16. 2 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/selection_cell.dart
  17. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/text_cell.dart
  18. 17 0
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  19. 7 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart
  20. 5 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart
  21. 193 32
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  22. 41 9
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  23. 36 36
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart
  24. 10 10
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart
  25. 2 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbenum.dart
  26. 2 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbjson.dart
  27. 5 3
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  28. 5 4
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  29. 1 0
      frontend/rust-lib/Cargo.lock
  30. 2 2
      frontend/rust-lib/dart-ffi/Cargo.toml
  31. 30 11
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  32. 7 3
      frontend/rust-lib/flowy-grid/src/event_map.rs
  33. 3 3
      frontend/rust-lib/flowy-grid/src/manager.rs
  34. 7 3
      frontend/rust-lib/flowy-grid/src/protobuf/model/dart_notification.rs
  35. 13 9
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  36. 1 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/dart_notification.proto
  37. 4 3
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  38. 36 34
      frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs
  39. 71 40
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  40. 20 20
      frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs
  41. 27 13
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  42. 1 0
      shared-lib/Cargo.lock
  43. 53 45
      shared-lib/flowy-collaboration/src/client_grid/grid_block_meta_pad.rs
  44. 7 7
      shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs
  45. 8 8
      shared-lib/flowy-collaboration/src/client_grid/grid_meta_pad.rs
  46. 2 2
      shared-lib/flowy-collaboration/src/client_grid/mod.rs
  47. 7 1
      shared-lib/flowy-error-code/src/code.rs
  48. 15 5
      shared-lib/flowy-error-code/src/protobuf/model/code.rs
  49. 4 1
      shared-lib/flowy-error-code/src/protobuf/proto/code.proto
  50. 1 0
      shared-lib/flowy-grid-data-model/Cargo.toml
  51. 40 8
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  52. 9 8
      shared-lib/flowy-grid-data-model/src/entities/meta.rs
  53. 1 0
      shared-lib/flowy-grid-data-model/src/lib.rs
  54. 82 0
      shared-lib/flowy-grid-data-model/src/parser/grid.rs
  55. 20 0
      shared-lib/flowy-grid-data-model/src/parser/id.rs
  56. 5 0
      shared-lib/flowy-grid-data-model/src/parser/mod.rs
  57. 704 139
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  58. 112 113
      shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs
  59. 15 4
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto
  60. 4 4
      shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto
  61. 2 2
      shared-lib/flowy-grid-data-model/tests/serde_test.rs

+ 8 - 10
frontend/app_flowy/lib/startup/home_deps_resolver.dart

@@ -17,8 +17,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
 import 'package:get_it/get_it.dart';
 
-import '../workspace/application/grid/grid_listener.dart';
-
 class HomeDepsResolver {
   static Future<void> resolve(GetIt getIt) async {
     getIt.registerFactoryParam<UserListener, UserProfile, void>(
@@ -93,13 +91,13 @@ class HomeDepsResolver {
 
     // Grid
     getIt.registerFactoryParam<GridBloc, View, void>(
-      (view, _) => GridBloc(view: view, service: GridService(), listener: GridListener(gridId: view.id)),
+      (view, _) => GridBloc(view: view, service: GridService()),
     );
 
     getIt.registerFactoryParam<RowBloc, GridRowData, void>(
       (data, _) => RowBloc(
-        service: RowService(data),
-        listener: RowListener(rowId: data.row.id),
+        rowService: RowService(data),
+        listener: RowListener(rowId: data.rowId),
       ),
     );
 
@@ -110,31 +108,31 @@ class HomeDepsResolver {
       ),
     );
 
-    getIt.registerFactoryParam<TextCellBloc, CellContext, void>(
+    getIt.registerFactoryParam<TextCellBloc, GridCellData, void>(
       (context, _) => TextCellBloc(
         service: CellService(context),
       ),
     );
 
-    getIt.registerFactoryParam<SelectionCellBloc, CellContext, void>(
+    getIt.registerFactoryParam<SelectionCellBloc, GridCellData, void>(
       (context, _) => SelectionCellBloc(
         service: CellService(context),
       ),
     );
 
-    getIt.registerFactoryParam<NumberCellBloc, CellContext, void>(
+    getIt.registerFactoryParam<NumberCellBloc, GridCellData, void>(
       (context, _) => NumberCellBloc(
         service: CellService(context),
       ),
     );
 
-    getIt.registerFactoryParam<DateCellBloc, CellContext, void>(
+    getIt.registerFactoryParam<DateCellBloc, GridCellData, void>(
       (context, _) => DateCellBloc(
         service: CellService(context),
       ),
     );
 
-    getIt.registerFactoryParam<CheckboxCellBloc, CellContext, void>(
+    getIt.registerFactoryParam<CheckboxCellBloc, GridCellData, void>(
       (context, _) => CheckboxCellBloc(
         service: CellService(context),
       ),

+ 2 - 11
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/cell_service.dart

@@ -1,11 +1,11 @@
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 
 class CellService {
-  final CellContext context;
+  final GridCellData context;
 
   CellService(this.context);
 
@@ -18,12 +18,3 @@ class CellService {
     return GridEventUpdateCell(payload).send();
   }
 }
-
-class CellContext {
-  final String gridId;
-  final String rowId;
-  final Field field;
-  final Cell? cell;
-
-  CellContext({required this.rowId, required this.gridId, required this.field, required this.cell});
-}

+ 0 - 43
frontend/app_flowy/lib/workspace/application/grid/data.dart

@@ -1,47 +1,4 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
-import 'package:equatable/equatable.dart';
-
-class GridInfo {
-  final String gridId;
-  List<GridBlock> gridBlocks;
-  List<Field> fields;
-
-  GridInfo({
-    required this.gridId,
-    required this.gridBlocks,
-    required this.fields,
-  });
-
-  GridRowData rowAtIndex(int index) {
-    final row = rows[index];
-    return GridRowData(
-      gridId: gridId,
-      row: row,
-      fields: fields,
-      cellMap: row.cellByFieldId,
-    );
-  }
-
-  int numberOfRows() {
-    return rows.length;
-  }
-}
-
-class GridRowData extends Equatable {
-  final String gridId;
-  final Row row;
-  final List<Field> fields;
-  final Map<String, Cell> cellMap;
-  const GridRowData({
-    required this.gridId,
-    required this.row,
-    required this.fields,
-    required this.cellMap,
-  });
-
-  @override
-  List<Object> get props => [row.hashCode, cellMap];
-}
 
 class GridColumnData {
   final List<Field> fields;

+ 54 - 48
frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart

@@ -1,14 +1,12 @@
 import 'dart:async';
-
 import 'package:dartz/dartz.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'data.dart';
-import 'grid_listener.dart';
+import 'grid_block_service.dart';
+import 'grid_listenr.dart';
 import 'grid_service.dart';
 
 part 'grid_bloc.freezed.dart';
@@ -16,14 +14,16 @@ part 'grid_bloc.freezed.dart';
 class GridBloc extends Bloc<GridEvent, GridState> {
   final View view;
   final GridService service;
-  final GridListener listener;
+  late GridListener _gridListener;
+  late GridBlockService _blockService;
+
+  GridBloc({required this.view, required this.service}) : super(GridState.initial()) {
+    _gridListener = GridListener();
 
-  GridBloc({required this.view, required this.service, required this.listener}) : super(GridState.initial()) {
     on<GridEvent>(
       (event, emit) async {
         await event.map(
           initial: (InitialGrid value) async {
-            await _startGridListening();
             await _loadGrid(emit);
           },
           createRow: (_CreateRow value) {
@@ -32,6 +32,9 @@ class GridBloc extends Bloc<GridEvent, GridState> {
           delete: (_Delete value) {},
           rename: (_Rename value) {},
           updateDesc: (_Desc value) {},
+          didLoadRows: (_DidLoadRows value) {
+            emit(state.copyWith(rows: value.rows));
+          },
         );
       },
     );
@@ -39,61 +42,63 @@ class GridBloc extends Bloc<GridEvent, GridState> {
 
   @override
   Future<void> close() async {
-    await listener.close();
+    await _gridListener.stop();
+    await _blockService.stop();
     return super.close();
   }
 
   Future<void> _startGridListening() async {
-    listener.blockUpdateNotifier.addPublishListener((result) {
-      result.fold((blockId) {
-        //
-        Log.info("$blockId");
-      }, (err) => null);
-    });
-
-    listener.start();
+    _blockService.didLoadRowscallback = (rows) {
+      add(GridEvent.didLoadRows(rows));
+    };
+
+    _gridListener.start();
   }
 
   Future<void> _loadGrid(Emitter<GridState> emit) async {
     final result = await service.openGrid(gridId: view.id);
-    result.fold(
-      (grid) {
-        _loadFields(grid, emit);
-        emit(state.copyWith(grid: Some(grid)));
-      },
-      (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
+
+    return Future(
+      () => result.fold(
+        (grid) async => await _loadFields(grid, emit),
+        (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
+      ),
     );
   }
 
   Future<void> _loadFields(Grid grid, Emitter<GridState> emit) async {
     final result = await service.getFields(gridId: grid.id, fieldOrders: grid.fieldOrders);
-    result.fold(
-      (fields) {
-        _loadGridBlocks(grid, emit);
-        emit(state.copyWith(fields: fields.items));
-      },
-      (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
+    return Future(
+      () => result.fold(
+        (fields) => _loadGridBlocks(grid, fields.items, emit),
+        (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
+      ),
     );
   }
 
-  Future<void> _loadGridBlocks(Grid grid, Emitter<GridState> emit) async {
-    final result = await service.getGridBlocks(gridId: grid.id, blocks: grid.blocks);
+  Future<void> _loadGridBlocks(Grid grid, List<Field> fields, Emitter<GridState> emit) async {
+    final result = await service.getGridBlocks(gridId: grid.id, blockOrders: grid.blockOrders);
+    result.fold(
+      (repeatedGridBlock) {
+        final gridBlocks = repeatedGridBlock.items;
+        final gridId = view.id;
+        _blockService = GridBlockService(
+          gridId: gridId,
+          fields: fields,
+          gridBlocks: gridBlocks,
+        );
+        final rows = _blockService.rows();
 
-    result.fold((repeatedGridBlock) {
-      final blocks = repeatedGridBlock.items;
-      final gridInfo = GridInfo(
-        gridId: grid.id,
-        blocks: blocks,
-        fields: _fields!,
-      );
-      emit(
-        state.copyWith(loadingState: GridLoadingState.finish(left(unit)), gridInfo: some(left(gridInfo))),
-      );
-    }, (err) {
-      emit(
-        state.copyWith(loadingState: GridLoadingState.finish(right(err)), gridInfo: none()),
-      );
-    });
+        _startGridListening();
+        emit(state.copyWith(
+          grid: Some(grid),
+          fields: Some(fields),
+          rows: rows,
+          loadingState: GridLoadingState.finish(left(unit)),
+        ));
+      },
+      (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)), rows: [])),
+    );
   }
 }
 
@@ -104,20 +109,21 @@ abstract class GridEvent with _$GridEvent {
   const factory GridEvent.updateDesc(String gridId, String desc) = _Desc;
   const factory GridEvent.delete(String gridId) = _Delete;
   const factory GridEvent.createRow() = _CreateRow;
+  const factory GridEvent.didLoadRows(List<GridRowData> rows) = _DidLoadRows;
 }
 
 @freezed
 abstract class GridState with _$GridState {
   const factory GridState({
     required GridLoadingState loadingState,
-    required List<Field> fields,
-    required List<Row> rows,
+    required Option<List<Field>> fields,
+    required List<GridRowData> rows,
     required Option<Grid> grid,
   }) = _GridState;
 
   factory GridState.initial() => GridState(
         loadingState: const _Loading(),
-        fields: [],
+        fields: none(),
         rows: [],
         grid: none(),
       );

+ 49 - 5
frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart → frontend/app_flowy/lib/workspace/application/grid/grid_block_service.dart

@@ -1,23 +1,67 @@
+import 'dart:collection';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/dart-notify/subject.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 import 'package:flowy_infra/notifier.dart';
 import 'dart:async';
 import 'dart:typed_data';
 import 'package:app_flowy/core/notification_helper.dart';
-import 'package:dartz/dartz.dart';
 
+import 'grid_service.dart';
+
+typedef DidLoadRowsCallback = void Function(List<GridRowData>);
 typedef GridBlockUpdateNotifiedValue = Either<GridBlockId, FlowyError>;
 
-class GridListener {
+class GridBlockService {
+  String gridId;
+  List<Field> fields;
+  LinkedHashMap<String, GridBlock> blockMap = LinkedHashMap();
+  late GridBlockListener _blockListener;
+  DidLoadRowsCallback? didLoadRowscallback;
+
+  GridBlockService({required this.gridId, required this.fields, required List<GridBlock> gridBlocks}) {
+    for (final gridBlock in gridBlocks) {
+      blockMap[gridBlock.blockId] = gridBlock;
+    }
+
+    _blockListener = GridBlockListener(gridId: gridId);
+    _blockListener.blockUpdateNotifier.addPublishListener((result) {
+      result.fold((blockId) {
+        //
+      }, (err) => null);
+    });
+  }
+
+  List<GridRowData> rows() {
+    List<GridRowData> rows = [];
+    blockMap.forEach((_, gridBlock) {
+      rows.addAll(gridBlock.rowIds.map(
+        (rowId) => GridRowData(
+          gridId: gridId,
+          fields: fields,
+          blockId: gridBlock.blockId,
+          rowId: rowId,
+        ),
+      ));
+    });
+    return rows;
+  }
+
+  Future<void> stop() async {
+    await _blockListener.stop();
+  }
+}
+
+class GridBlockListener {
   final String gridId;
   PublishNotifier<GridBlockUpdateNotifiedValue> blockUpdateNotifier = PublishNotifier<GridBlockUpdateNotifiedValue>();
   StreamSubscription<SubscribeObject>? _subscription;
   late GridNotificationParser _parser;
 
-  GridListener({required this.gridId});
+  GridBlockListener({required this.gridId});
 
   void start() {
     _parser = GridNotificationParser(
@@ -44,7 +88,7 @@ class GridListener {
     }
   }
 
-  Future<void> close() async {
+  Future<void> stop() async {
     await _subscription?.cancel();
     blockUpdateNotifier.dispose();
   }

+ 4 - 0
frontend/app_flowy/lib/workspace/application/grid/grid_listenr.dart

@@ -0,0 +1,4 @@
+class GridListener {
+  void start() {}
+  Future<void> stop() async {}
+}

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

@@ -3,7 +3,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:dartz/dartz.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
+import 'package:equatable/equatable.dart';
 
 class GridService {
   Future<Either<Grid, FlowyError>> openGrid({required String gridId}) async {
@@ -15,15 +15,15 @@ class GridService {
 
   Future<Either<Row, FlowyError>> createRow({required String gridId, Option<String>? upperRowId}) {
     CreateRowPayload payload = CreateRowPayload.create()..gridId = gridId;
-    upperRowId?.fold(() => null, (id) => payload.upperRowId = id);
+    upperRowId?.fold(() => null, (id) => payload.startRowId = id);
     return GridEventCreateRow(payload).send();
   }
 
   Future<Either<RepeatedGridBlock, FlowyError>> getGridBlocks(
-      {required String gridId, required List<GridBlockMeta> blocks}) {
+      {required String gridId, required List<GridBlockOrder> blockOrders}) {
     final payload = QueryGridBlocksPayload.create()
       ..gridId = gridId
-      ..blocks.addAll(blocks);
+      ..blockOrders.addAll(blockOrders);
     return GridEventGetGridBlocks(payload).send();
   }
 
@@ -34,3 +34,19 @@ class GridService {
     return GridEventGetFields(payload).send();
   }
 }
+
+class GridRowData extends Equatable {
+  final String gridId;
+  final String rowId;
+  final String blockId;
+  final List<Field> fields;
+  const GridRowData({
+    required this.gridId,
+    required this.rowId,
+    required this.blockId,
+    required this.fields,
+  });
+
+  @override
+  List<Object> get props => [rowId];
+}

+ 33 - 6
frontend/app_flowy/lib/workspace/application/grid/row_bloc.dart

@@ -2,25 +2,26 @@ import 'package:flowy_sdk/log.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import 'data.dart';
+import 'grid_service.dart';
 import 'row_listener.dart';
 import 'row_service.dart';
 
 part 'row_bloc.freezed.dart';
 
 class RowBloc extends Bloc<RowEvent, RowState> {
-  final RowService service;
+  final RowService rowService;
   final RowListener listener;
 
-  RowBloc({required this.service, required this.listener}) : super(RowState.initial(service.rowData)) {
+  RowBloc({required this.rowService, required this.listener}) : super(RowState.initial(rowService.rowData)) {
     on<RowEvent>(
       (event, emit) async {
         await event.map(
           initial: (_InitialRow value) async {
             _startRowListening();
+            await _loadCellDatas(emit);
           },
           createRow: (_CreateRow value) {
-            service.createRow();
+            rowService.createRow();
           },
           activeRow: (_ActiveRow value) {
             emit(state.copyWith(active: true));
@@ -55,6 +56,25 @@ class RowBloc extends Bloc<RowEvent, RowState> {
 
     listener.start();
   }
+
+  Future<void> _loadCellDatas(Emitter<RowState> emit) async {
+    final result = await rowService.getRow();
+    result.fold(
+      (row) {
+        final cellDatas = rowService.rowData.fields.map((field) {
+          final cell = row.cellByFieldId[field.id];
+          return GridCellData(
+            rowId: row.id,
+            gridId: rowService.rowData.gridId,
+            cell: cell,
+            field: field,
+          );
+        }).toList();
+        emit(state.copyWith(cellDatas: cellDatas, rowHeight: row.height.toDouble()));
+      },
+      (e) => Log.error(e),
+    );
+  }
 }
 
 @freezed
@@ -68,9 +88,16 @@ abstract class RowEvent with _$RowEvent {
 @freezed
 abstract class RowState with _$RowState {
   const factory RowState({
-    required GridRowData data,
+    required String rowId,
+    required double rowHeight,
+    required List<GridCellData> cellDatas,
     required bool active,
   }) = _RowState;
 
-  factory RowState.initial(GridRowData data) => RowState(data: data, active: false);
+  factory RowState.initial(GridRowData data) => RowState(
+        rowId: data.rowId,
+        active: false,
+        rowHeight: 0,
+        cellDatas: [],
+      );
 }

+ 26 - 2
frontend/app_flowy/lib/workspace/application/grid/row_service.dart

@@ -1,9 +1,10 @@
-import 'package:app_flowy/workspace/application/grid/data.dart';
 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-data-model/grid.pb.dart';
 
+import 'grid_service.dart';
+
 class RowService {
   final GridRowData rowData;
 
@@ -12,8 +13,31 @@ class RowService {
   Future<Either<Row, FlowyError>> createRow() {
     CreateRowPayload payload = CreateRowPayload.create()
       ..gridId = rowData.gridId
-      ..upperRowId = rowData.row.id;
+      ..startRowId = rowData.rowId;
 
     return GridEventCreateRow(payload).send();
   }
+
+  Future<Either<Row, FlowyError>> getRow() {
+    QueryRowPayload payload = QueryRowPayload.create()
+      ..gridId = rowData.gridId
+      ..blockId = rowData.blockId
+      ..rowId = rowData.rowId;
+
+    return GridEventGetRow(payload).send();
+  }
+}
+
+class GridCellData {
+  final String gridId;
+  final String rowId;
+  final Field field;
+  final Cell? cell;
+
+  GridCellData({
+    required this.rowId,
+    required this.gridId,
+    required this.field,
+    required this.cell,
+  });
 }

+ 15 - 19
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -1,5 +1,4 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/data.dart';
 import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
@@ -41,7 +40,7 @@ class _GridPageState extends State<GridPage> {
           return state.loadingState.map(
             loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
             finish: (result) => result.successOrFail.fold(
-              (_) => const GridBody(),
+              (_) => const FlowyGrid(),
               (err) => FlowyErrorPage(err.toString()),
             ),
           );
@@ -66,14 +65,14 @@ class _GridPageState extends State<GridPage> {
   }
 }
 
-class GridBody extends StatefulWidget {
-  const GridBody({Key? key}) : super(key: key);
+class FlowyGrid extends StatefulWidget {
+  const FlowyGrid({Key? key}) : super(key: key);
 
   @override
-  _GridBodyState createState() => _GridBodyState();
+  _FlowyGridState createState() => _FlowyGridState();
 }
 
-class _GridBodyState extends State<GridBody> {
+class _FlowyGridState extends State<FlowyGrid> {
   final _scrollController = GridScrollController();
 
   @override
@@ -86,31 +85,28 @@ class _GridBodyState extends State<GridBody> {
   Widget build(BuildContext context) {
     return BlocBuilder<GridBloc, GridState>(
       builder: (context, state) {
-        return state.gridInfo.fold(
+        return state.fields.fold(
           () => const Center(child: CircularProgressIndicator.adaptive()),
-          (some) => some.fold(
-            (gridInfo) => _renderGrid(context, gridInfo),
-            (err) => FlowyErrorPage(err.toString()),
-          ),
+          (fields) => _renderGrid(context, fields),
         );
       },
     );
   }
 
-  Widget _renderGrid(BuildContext context, GridInfo gridInfo) {
+  Widget _renderGrid(BuildContext context, List<Field> fields) {
     return Stack(
       children: [
         StyledSingleChildScrollView(
           controller: _scrollController.horizontalController,
           axis: Axis.horizontal,
           child: SizedBox(
-            width: GridLayout.headerWidth(gridInfo.fields),
+            width: GridLayout.headerWidth(fields),
             child: CustomScrollView(
               physics: StyledScrollPhysics(),
               controller: _scrollController.verticalController,
               slivers: <Widget>[
-                _buildHeader(gridInfo.fields),
-                _buildRows(gridInfo),
+                _buildHeader(fields),
+                _buildRows(context),
                 const GridFooter(),
               ],
             ),
@@ -134,14 +130,14 @@ class _GridBodyState extends State<GridBody> {
     );
   }
 
-  Widget _buildRows(GridInfo gridInfo) {
+  Widget _buildRows(BuildContext context) {
     return SliverList(
       delegate: SliverChildBuilderDelegate(
         (context, index) {
-          final data = gridInfo.rowAtIndex(index);
-          return GridRowWidget(data: data);
+          final rowData = context.read<GridBloc>().state.rows[index];
+          return GridRowWidget(data: rowData);
         },
-        childCount: gridInfo.numberOfRows(),
+        childCount: context.read<GridBloc>().state.rows.length,
         addRepaintBoundaries: true,
       ),
     );

+ 9 - 8
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_builder.dart

@@ -1,4 +1,5 @@
 import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_service.dart';
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/widgets.dart';
 import 'checkbox_cell.dart';
@@ -7,20 +8,20 @@ import 'number_cell.dart';
 import 'selection_cell.dart';
 import 'text_cell.dart';
 
-Widget buildGridCell(CellContext cellContext) {
-  switch (cellContext.field.fieldType) {
+Widget buildGridCell(GridCellData cellData) {
+  switch (cellData.field.fieldType) {
     case FieldType.Checkbox:
-      return CheckboxCell(cellContext: cellContext);
+      return CheckboxCell(cellData: cellData);
     case FieldType.DateTime:
-      return DateCell(cellContext: cellContext);
+      return DateCell(cellData: cellData);
     case FieldType.MultiSelect:
-      return MultiSelectCell(cellContext: cellContext);
+      return MultiSelectCell(cellContext: cellData);
     case FieldType.Number:
-      return NumberCell(cellContext: cellContext);
+      return NumberCell(cellData: cellData);
     case FieldType.RichText:
-      return GridTextCell(cellContext: cellContext);
+      return GridTextCell(cellData: cellData);
     case FieldType.SingleSelect:
-      return SingleSelectCell(cellContext: cellContext);
+      return SingleSelectCell(cellContext: cellData);
     default:
       return const BlankCell();
   }

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/checkbox_cell.dart

@@ -1,14 +1,14 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 class CheckboxCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellData;
 
   const CheckboxCell({
-    required this.cellContext,
+    required this.cellData,
     Key? key,
   }) : super(key: key);
 
@@ -21,7 +21,7 @@ class _CheckboxCellState extends State<CheckboxCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<CheckboxCellBloc>(param1: widget.cellContext);
+    _cellBloc = getIt<CheckboxCellBloc>(param1: widget.cellData);
     super.initState();
   }
 

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/date_cell.dart

@@ -1,14 +1,14 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/date_cell_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 class DateCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellData;
 
   const DateCell({
-    required this.cellContext,
+    required this.cellData,
     Key? key,
   }) : super(key: key);
 
@@ -21,7 +21,7 @@ class _DateCellState extends State<DateCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<DateCellBloc>(param1: widget.cellContext);
+    _cellBloc = getIt<DateCellBloc>(param1: widget.cellData);
     super.initState();
   }
 

+ 10 - 19
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart

@@ -11,7 +11,7 @@ import 'cell_container.dart';
 
 class GridRowWidget extends StatefulWidget {
   final GridRowData data;
-  GridRowWidget({required this.data, Key? key}) : super(key: ObjectKey(data.row.id));
+  GridRowWidget({required this.data, Key? key}) : super(key: ObjectKey(data.rowId));
 
   @override
   State<GridRowWidget> createState() => _GridRowWidgetState();
@@ -37,7 +37,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
           onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
           onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
           child: SizedBox(
-            height: _rowBloc.state.data.row.height.toDouble(),
+            height: _rowBloc.state.rowHeight,
             child: Row(
               crossAxisAlignment: CrossAxisAlignment.stretch,
               children: [
@@ -60,26 +60,17 @@ class _GridRowWidgetState extends State<GridRowWidget> {
 
   Widget _buildCells() {
     return BlocBuilder<RowBloc, RowState>(
-      buildWhen: (p, c) => p.data != c.data,
+      buildWhen: (p, c) => p.cellDatas != c.cellDatas,
       builder: (context, state) {
         return Row(
-          key: ValueKey(state.data.row.id),
-          children: state.data.fields.map(
-            (field) {
-              final cell = state.data.cellMap[field.id];
-              return CellContainer(
-                width: field.width.toDouble(),
-                child: buildGridCell(
-                  CellContext(
-                    gridId: state.data.gridId,
-                    rowId: state.data.row.id,
-                    field: field,
-                    cell: cell,
-                  ),
+          children: state.cellDatas
+              .map(
+                (cellData) => CellContainer(
+                  width: cellData.field.width.toDouble(),
+                  child: buildGridCell(cellData),
                 ),
-              );
-            },
-          ).toList(),
+              )
+              .toList(),
         );
       },
     );

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/number_cell.dart

@@ -1,14 +1,14 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/number_cell_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 class NumberCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellData;
 
   const NumberCell({
-    required this.cellContext,
+    required this.cellData,
     Key? key,
   }) : super(key: key);
 
@@ -21,7 +21,7 @@ class _NumberCellState extends State<NumberCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<NumberCellBloc>(param1: widget.cellContext);
+    _cellBloc = getIt<NumberCellBloc>(param1: widget.cellData);
     super.initState();
   }
 

+ 2 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/selection_cell.dart

@@ -3,7 +3,7 @@ import 'package:app_flowy/workspace/application/grid/prelude.dart';
 import 'package:flutter/material.dart';
 
 class SingleSelectCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellContext;
 
   const SingleSelectCell({
     required this.cellContext,
@@ -37,7 +37,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
 
 //----------------------------------------------------------------
 class MultiSelectCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellContext;
 
   const MultiSelectCell({
     required this.cellContext,

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/text_cell.dart

@@ -1,15 +1,15 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_service.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/text_cell_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/row_service.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 /// The interface of base cell.
 
 class GridTextCell extends StatefulWidget {
-  final CellContext cellContext;
+  final GridCellData cellData;
   const GridTextCell({
-    required this.cellContext,
+    required this.cellData,
     Key? key,
   }) : super(key: key);
 
@@ -24,7 +24,7 @@ class _GridTextCellState extends State<GridTextCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<TextCellBloc>(param1: widget.cellContext);
+    _cellBloc = getIt<TextCellBloc>(param1: widget.cellData);
     _controller = TextEditingController(text: _cellBloc.state.content);
     _focusNode.addListener(_focusChanged);
     super.initState();

+ 17 - 0
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart

@@ -69,6 +69,23 @@ class GridEventCreateRow {
     }
 }
 
+class GridEventGetRow {
+     QueryRowPayload request;
+     GridEventGetRow(this.request);
+
+    Future<Either<Row, FlowyError>> send() {
+    final request = FFIRequest.create()
+          ..event = GridEvent.GetRow.toString()
+          ..payload = requestToBytes(this.request);
+
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (okBytes) => left(Row.fromBuffer(okBytes)),
+           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+        ));
+    }
+}
+
 class GridEventUpdateCell {
      CellMetaChangeset request;
      GridEventUpdateCell(this.request);

+ 7 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart

@@ -41,7 +41,10 @@ class ErrorCode extends $pb.ProtobufEnum {
   static const ErrorCode UserIdInvalid = ErrorCode._(311, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserIdInvalid');
   static const ErrorCode UserNotExist = ErrorCode._(312, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNotExist');
   static const ErrorCode TextTooLong = ErrorCode._(400, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TextTooLong');
-  static const ErrorCode InvalidData = ErrorCode._(401, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
+  static const ErrorCode BlockIdIsEmpty = ErrorCode._(401, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'BlockIdIsEmpty');
+  static const ErrorCode RowIdIsEmpty = ErrorCode._(402, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RowIdIsEmpty');
+  static const ErrorCode GridIdIsEmpty = ErrorCode._(403, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridIdIsEmpty');
+  static const ErrorCode InvalidData = ErrorCode._(404, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
 
   static const $core.List<ErrorCode> values = <ErrorCode> [
     Internal,
@@ -75,6 +78,9 @@ class ErrorCode extends $pb.ProtobufEnum {
     UserIdInvalid,
     UserNotExist,
     TextTooLong,
+    BlockIdIsEmpty,
+    RowIdIsEmpty,
+    GridIdIsEmpty,
     InvalidData,
   ];
 

+ 5 - 2
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart

@@ -43,9 +43,12 @@ const ErrorCode$json = const {
     const {'1': 'UserIdInvalid', '2': 311},
     const {'1': 'UserNotExist', '2': 312},
     const {'1': 'TextTooLong', '2': 400},
-    const {'1': 'InvalidData', '2': 401},
+    const {'1': 'BlockIdIsEmpty', '2': 401},
+    const {'1': 'RowIdIsEmpty', '2': 402},
+    const {'1': 'GridIdIsEmpty', '2': 403},
+    const {'1': 'InvalidData', '2': 404},
   ],
 };
 
 /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIYChRXb3Jrc3BhY2VOYW1lSW52YWxpZBBkEhYKEldvcmtzcGFjZUlkSW52YWxpZBBlEhgKFEFwcENvbG9yU3R5bGVJbnZhbGlkEGYSGAoUV29ya3NwYWNlRGVzY1Rvb0xvbmcQZxIYChRXb3Jrc3BhY2VOYW1lVG9vTG9uZxBoEhAKDEFwcElkSW52YWxpZBBuEhIKDkFwcE5hbWVJbnZhbGlkEG8SEwoPVmlld05hbWVJbnZhbGlkEHgSGAoUVmlld1RodW1ibmFpbEludmFsaWQQeRIRCg1WaWV3SWRJbnZhbGlkEHoSEwoPVmlld0Rlc2NUb29Mb25nEHsSEwoPVmlld0RhdGFJbnZhbGlkEHwSEwoPVmlld05hbWVUb29Mb25nEH0SEQoMQ29ubmVjdEVycm9yEMgBEhEKDEVtYWlsSXNFbXB0eRCsAhIXChJFbWFpbEZvcm1hdEludmFsaWQQrQISFwoSRW1haWxBbHJlYWR5RXhpc3RzEK4CEhQKD1Bhc3N3b3JkSXNFbXB0eRCvAhIUCg9QYXNzd29yZFRvb0xvbmcQsAISJQogUGFzc3dvcmRDb250YWluc0ZvcmJpZENoYXJhY3RlcnMQsQISGgoVUGFzc3dvcmRGb3JtYXRJbnZhbGlkELICEhUKEFBhc3N3b3JkTm90TWF0Y2gQswISFAoPVXNlck5hbWVUb29Mb25nELQCEicKIlVzZXJOYW1lQ29udGFpbkZvcmJpZGRlbkNoYXJhY3RlcnMQtQISFAoPVXNlck5hbWVJc0VtcHR5ELYCEhIKDVVzZXJJZEludmFsaWQQtwISEQoMVXNlck5vdEV4aXN0ELgCEhAKC1RleHRUb29Mb25nEJADEhAKC0ludmFsaWREYXRhEJED');
+final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIYChRXb3Jrc3BhY2VOYW1lSW52YWxpZBBkEhYKEldvcmtzcGFjZUlkSW52YWxpZBBlEhgKFEFwcENvbG9yU3R5bGVJbnZhbGlkEGYSGAoUV29ya3NwYWNlRGVzY1Rvb0xvbmcQZxIYChRXb3Jrc3BhY2VOYW1lVG9vTG9uZxBoEhAKDEFwcElkSW52YWxpZBBuEhIKDkFwcE5hbWVJbnZhbGlkEG8SEwoPVmlld05hbWVJbnZhbGlkEHgSGAoUVmlld1RodW1ibmFpbEludmFsaWQQeRIRCg1WaWV3SWRJbnZhbGlkEHoSEwoPVmlld0Rlc2NUb29Mb25nEHsSEwoPVmlld0RhdGFJbnZhbGlkEHwSEwoPVmlld05hbWVUb29Mb25nEH0SEQoMQ29ubmVjdEVycm9yEMgBEhEKDEVtYWlsSXNFbXB0eRCsAhIXChJFbWFpbEZvcm1hdEludmFsaWQQrQISFwoSRW1haWxBbHJlYWR5RXhpc3RzEK4CEhQKD1Bhc3N3b3JkSXNFbXB0eRCvAhIUCg9QYXNzd29yZFRvb0xvbmcQsAISJQogUGFzc3dvcmRDb250YWluc0ZvcmJpZENoYXJhY3RlcnMQsQISGgoVUGFzc3dvcmRGb3JtYXRJbnZhbGlkELICEhUKEFBhc3N3b3JkTm90TWF0Y2gQswISFAoPVXNlck5hbWVUb29Mb25nELQCEicKIlVzZXJOYW1lQ29udGFpbkZvcmJpZGRlbkNoYXJhY3RlcnMQtQISFAoPVXNlck5hbWVJc0VtcHR5ELYCEhIKDVVzZXJJZEludmFsaWQQtwISEQoMVXNlck5vdEV4aXN0ELgCEhAKC1RleHRUb29Mb25nEJADEhMKDkJsb2NrSWRJc0VtcHR5EJEDEhEKDFJvd0lkSXNFbXB0eRCSAxISCg1HcmlkSWRJc0VtcHR5EJMDEhAKC0ludmFsaWREYXRhEJQD');

+ 193 - 32
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart

@@ -9,15 +9,13 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
-import 'meta.pb.dart' as $0;
-
 import 'meta.pbenum.dart' as $0;
 
 class Grid extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Grid', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..pc<FieldOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldOrders', $pb.PbFieldType.PM, subBuilder: FieldOrder.create)
-    ..pc<$0.GridBlockMeta>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: $0.GridBlockMeta.create)
+    ..pc<GridBlockOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockOrders', $pb.PbFieldType.PM, subBuilder: GridBlockOrder.create)
     ..hasRequiredFields = false
   ;
 
@@ -25,7 +23,7 @@ class Grid extends $pb.GeneratedMessage {
   factory Grid({
     $core.String? id,
     $core.Iterable<FieldOrder>? fieldOrders,
-    $core.Iterable<$0.GridBlockMeta>? blocks,
+    $core.Iterable<GridBlockOrder>? blockOrders,
   }) {
     final _result = create();
     if (id != null) {
@@ -34,8 +32,8 @@ class Grid extends $pb.GeneratedMessage {
     if (fieldOrders != null) {
       _result.fieldOrders.addAll(fieldOrders);
     }
-    if (blocks != null) {
-      _result.blocks.addAll(blocks);
+    if (blockOrders != null) {
+      _result.blockOrders.addAll(blockOrders);
     }
     return _result;
   }
@@ -73,7 +71,7 @@ class Grid extends $pb.GeneratedMessage {
   $core.List<FieldOrder> get fieldOrders => $_getList(1);
 
   @$pb.TagNumber(3)
-  $core.List<$0.GridBlockMeta> get blocks => $_getList(2);
+  $core.List<GridBlockOrder> get blockOrders => $_getList(2);
 }
 
 class Field extends $pb.GeneratedMessage {
@@ -507,6 +505,47 @@ class Row extends $pb.GeneratedMessage {
   void clearHeight() => clearField(3);
 }
 
+class RepeatedRow extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedRow', createEmptyInstance: create)
+    ..pc<Row>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Row.create)
+    ..hasRequiredFields = false
+  ;
+
+  RepeatedRow._() : super();
+  factory RepeatedRow({
+    $core.Iterable<Row>? items,
+  }) {
+    final _result = create();
+    if (items != null) {
+      _result.items.addAll(items);
+    }
+    return _result;
+  }
+  factory RepeatedRow.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RepeatedRow.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  RepeatedRow clone() => RepeatedRow()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RepeatedRow copyWith(void Function(RepeatedRow) updates) => super.copyWith((message) => updates(message as RepeatedRow)) as RepeatedRow; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RepeatedRow create() => RepeatedRow._();
+  RepeatedRow createEmptyInstance() => create();
+  static $pb.PbList<RepeatedRow> createRepeated() => $pb.PbList<RepeatedRow>();
+  @$core.pragma('dart2js:noInline')
+  static RepeatedRow getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedRow>(create);
+  static RepeatedRow? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.List<Row> get items => $_getList(0);
+}
+
 class RepeatedGridBlock extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedGridBlock', createEmptyInstance: create)
     ..pc<GridBlock>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: GridBlock.create)
@@ -548,24 +587,71 @@ class RepeatedGridBlock extends $pb.GeneratedMessage {
   $core.List<GridBlock> get items => $_getList(0);
 }
 
+class GridBlockOrder extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockOrder', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..hasRequiredFields = false
+  ;
+
+  GridBlockOrder._() : super();
+  factory GridBlockOrder({
+    $core.String? blockId,
+  }) {
+    final _result = create();
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    return _result;
+  }
+  factory GridBlockOrder.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GridBlockOrder.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  GridBlockOrder clone() => GridBlockOrder()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GridBlockOrder copyWith(void Function(GridBlockOrder) updates) => super.copyWith((message) => updates(message as GridBlockOrder)) as GridBlockOrder; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GridBlockOrder create() => GridBlockOrder._();
+  GridBlockOrder createEmptyInstance() => create();
+  static $pb.PbList<GridBlockOrder> createRepeated() => $pb.PbList<GridBlockOrder>();
+  @$core.pragma('dart2js:noInline')
+  static GridBlockOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlockOrder>(create);
+  static GridBlockOrder? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get blockId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set blockId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasBlockId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearBlockId() => clearField(1);
+}
+
 class GridBlock extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlock', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
-    ..pc<Row>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rows', $pb.PbFieldType.PM, subBuilder: Row.create)
+    ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowIds')
     ..hasRequiredFields = false
   ;
 
   GridBlock._() : super();
   factory GridBlock({
     $core.String? blockId,
-    $core.Iterable<Row>? rows,
+    $core.Iterable<$core.String>? rowIds,
   }) {
     final _result = create();
     if (blockId != null) {
       _result.blockId = blockId;
     }
-    if (rows != null) {
-      _result.rows.addAll(rows);
+    if (rowIds != null) {
+      _result.rowIds.addAll(rowIds);
     }
     return _result;
   }
@@ -600,7 +686,7 @@ class GridBlock extends $pb.GeneratedMessage {
   void clearBlockId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.List<Row> get rows => $_getList(1);
+  $core.List<$core.String> get rowIds => $_getList(1);
 }
 
 class Cell extends $pb.GeneratedMessage {
@@ -846,34 +932,34 @@ class GridBlockId extends $pb.GeneratedMessage {
   void clearValue() => clearField(1);
 }
 
-enum CreateRowPayload_OneOfUpperRowId {
-  upperRowId, 
+enum CreateRowPayload_OneOfStartRowId {
+  startRowId, 
   notSet
 }
 
 class CreateRowPayload extends $pb.GeneratedMessage {
-  static const $core.Map<$core.int, CreateRowPayload_OneOfUpperRowId> _CreateRowPayload_OneOfUpperRowIdByTag = {
-    2 : CreateRowPayload_OneOfUpperRowId.upperRowId,
-    0 : CreateRowPayload_OneOfUpperRowId.notSet
+  static const $core.Map<$core.int, CreateRowPayload_OneOfStartRowId> _CreateRowPayload_OneOfStartRowIdByTag = {
+    2 : CreateRowPayload_OneOfStartRowId.startRowId,
+    0 : CreateRowPayload_OneOfStartRowId.notSet
   };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateRowPayload', createEmptyInstance: create)
     ..oo(0, [2])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'upperRowId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startRowId')
     ..hasRequiredFields = false
   ;
 
   CreateRowPayload._() : super();
   factory CreateRowPayload({
     $core.String? gridId,
-    $core.String? upperRowId,
+    $core.String? startRowId,
   }) {
     final _result = create();
     if (gridId != null) {
       _result.gridId = gridId;
     }
-    if (upperRowId != null) {
-      _result.upperRowId = upperRowId;
+    if (startRowId != null) {
+      _result.startRowId = startRowId;
     }
     return _result;
   }
@@ -898,8 +984,8 @@ class CreateRowPayload extends $pb.GeneratedMessage {
   static CreateRowPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateRowPayload>(create);
   static CreateRowPayload? _defaultInstance;
 
-  CreateRowPayload_OneOfUpperRowId whichOneOfUpperRowId() => _CreateRowPayload_OneOfUpperRowIdByTag[$_whichOneof(0)]!;
-  void clearOneOfUpperRowId() => clearField($_whichOneof(0));
+  CreateRowPayload_OneOfStartRowId whichOneOfStartRowId() => _CreateRowPayload_OneOfStartRowIdByTag[$_whichOneof(0)]!;
+  void clearOneOfStartRowId() => clearField($_whichOneof(0));
 
   @$pb.TagNumber(1)
   $core.String get gridId => $_getSZ(0);
@@ -911,13 +997,13 @@ class CreateRowPayload extends $pb.GeneratedMessage {
   void clearGridId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.String get upperRowId => $_getSZ(1);
+  $core.String get startRowId => $_getSZ(1);
   @$pb.TagNumber(2)
-  set upperRowId($core.String v) { $_setString(1, v); }
+  set startRowId($core.String v) { $_setString(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasUpperRowId() => $_has(1);
+  $core.bool hasStartRowId() => $_has(1);
   @$pb.TagNumber(2)
-  void clearUpperRowId() => clearField(2);
+  void clearStartRowId() => clearField(2);
 }
 
 class QueryFieldPayload extends $pb.GeneratedMessage {
@@ -986,21 +1072,21 @@ class QueryFieldPayload extends $pb.GeneratedMessage {
 class QueryGridBlocksPayload extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryGridBlocksPayload', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..pc<$0.GridBlockMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: $0.GridBlockMeta.create)
+    ..pc<GridBlockOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockOrders', $pb.PbFieldType.PM, subBuilder: GridBlockOrder.create)
     ..hasRequiredFields = false
   ;
 
   QueryGridBlocksPayload._() : super();
   factory QueryGridBlocksPayload({
     $core.String? gridId,
-    $core.Iterable<$0.GridBlockMeta>? blocks,
+    $core.Iterable<GridBlockOrder>? blockOrders,
   }) {
     final _result = create();
     if (gridId != null) {
       _result.gridId = gridId;
     }
-    if (blocks != null) {
-      _result.blocks.addAll(blocks);
+    if (blockOrders != null) {
+      _result.blockOrders.addAll(blockOrders);
     }
     return _result;
   }
@@ -1035,6 +1121,81 @@ class QueryGridBlocksPayload extends $pb.GeneratedMessage {
   void clearGridId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.List<$0.GridBlockMeta> get blocks => $_getList(1);
+  $core.List<GridBlockOrder> get blockOrders => $_getList(1);
+}
+
+class QueryRowPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryRowPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
+    ..hasRequiredFields = false
+  ;
+
+  QueryRowPayload._() : super();
+  factory QueryRowPayload({
+    $core.String? gridId,
+    $core.String? blockId,
+    $core.String? rowId,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    if (rowId != null) {
+      _result.rowId = rowId;
+    }
+    return _result;
+  }
+  factory QueryRowPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory QueryRowPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  QueryRowPayload clone() => QueryRowPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  QueryRowPayload copyWith(void Function(QueryRowPayload) updates) => super.copyWith((message) => updates(message as QueryRowPayload)) as QueryRowPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static QueryRowPayload create() => QueryRowPayload._();
+  QueryRowPayload createEmptyInstance() => create();
+  static $pb.PbList<QueryRowPayload> createRepeated() => $pb.PbList<QueryRowPayload>();
+  @$core.pragma('dart2js:noInline')
+  static QueryRowPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<QueryRowPayload>(create);
+  static QueryRowPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get gridId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set gridId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGridId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGridId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get blockId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set blockId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasBlockId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearBlockId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get rowId => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set rowId($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRowId() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearRowId() => clearField(3);
 }
 

+ 41 - 9
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart

@@ -14,12 +14,12 @@ const Grid$json = const {
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'field_orders', '3': 2, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'fieldOrders'},
-    const {'1': 'blocks', '3': 3, '4': 3, '5': 11, '6': '.GridBlockMeta', '10': 'blocks'},
+    const {'1': 'block_orders', '3': 3, '4': 3, '5': 11, '6': '.GridBlockOrder', '10': 'blockOrders'},
   ],
 };
 
 /// Descriptor for `Grid`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridDescriptor = $convert.base64Decode('CgRHcmlkEg4KAmlkGAEgASgJUgJpZBIuCgxmaWVsZF9vcmRlcnMYAiADKAsyCy5GaWVsZE9yZGVyUgtmaWVsZE9yZGVycxImCgZibG9ja3MYAyADKAsyDi5HcmlkQmxvY2tNZXRhUgZibG9ja3M=');
+final $typed_data.Uint8List gridDescriptor = $convert.base64Decode('CgRHcmlkEg4KAmlkGAEgASgJUgJpZBIuCgxmaWVsZF9vcmRlcnMYAiADKAsyCy5GaWVsZE9yZGVyUgtmaWVsZE9yZGVycxIyCgxibG9ja19vcmRlcnMYAyADKAsyDy5HcmlkQmxvY2tPcmRlclILYmxvY2tPcmRlcnM=');
 @$core.Deprecated('Use fieldDescriptor instead')
 const Field$json = const {
   '1': 'Field',
@@ -110,6 +110,16 @@ const Row_CellByFieldIdEntry$json = const {
 
 /// Descriptor for `Row`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List rowDescriptor = $convert.base64Decode('CgNSb3cSDgoCaWQYASABKAlSAmlkEkAKEGNlbGxfYnlfZmllbGRfaWQYAiADKAsyFy5Sb3cuQ2VsbEJ5RmllbGRJZEVudHJ5Ug1jZWxsQnlGaWVsZElkEhYKBmhlaWdodBgDIAEoBVIGaGVpZ2h0GkcKEkNlbGxCeUZpZWxkSWRFbnRyeRIQCgNrZXkYASABKAlSA2tleRIbCgV2YWx1ZRgCIAEoCzIFLkNlbGxSBXZhbHVlOgI4AQ==');
+@$core.Deprecated('Use repeatedRowDescriptor instead')
+const RepeatedRow$json = const {
+  '1': 'RepeatedRow',
+  '2': const [
+    const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Row', '10': 'items'},
+  ],
+};
+
+/// Descriptor for `RepeatedRow`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List repeatedRowDescriptor = $convert.base64Decode('CgtSZXBlYXRlZFJvdxIaCgVpdGVtcxgBIAMoCzIELlJvd1IFaXRlbXM=');
 @$core.Deprecated('Use repeatedGridBlockDescriptor instead')
 const RepeatedGridBlock$json = const {
   '1': 'RepeatedGridBlock',
@@ -120,17 +130,27 @@ const RepeatedGridBlock$json = const {
 
 /// Descriptor for `RepeatedGridBlock`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List repeatedGridBlockDescriptor = $convert.base64Decode('ChFSZXBlYXRlZEdyaWRCbG9jaxIgCgVpdGVtcxgBIAMoCzIKLkdyaWRCbG9ja1IFaXRlbXM=');
+@$core.Deprecated('Use gridBlockOrderDescriptor instead')
+const GridBlockOrder$json = const {
+  '1': 'GridBlockOrder',
+  '2': const [
+    const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
+  ],
+};
+
+/// Descriptor for `GridBlockOrder`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List gridBlockOrderDescriptor = $convert.base64Decode('Cg5HcmlkQmxvY2tPcmRlchIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZA==');
 @$core.Deprecated('Use gridBlockDescriptor instead')
 const GridBlock$json = const {
   '1': 'GridBlock',
   '2': const [
     const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
-    const {'1': 'rows', '3': 2, '4': 3, '5': 11, '6': '.Row', '10': 'rows'},
+    const {'1': 'row_ids', '3': 2, '4': 3, '5': 9, '10': 'rowIds'},
   ],
 };
 
 /// Descriptor for `GridBlock`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSGAoEcm93cxgCIAMoCzIELlJvd1IEcm93cw==');
+final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSFwoHcm93X2lkcxgCIAMoCVIGcm93SWRz');
 @$core.Deprecated('Use cellDescriptor instead')
 const Cell$json = const {
   '1': 'Cell',
@@ -187,15 +207,15 @@ const CreateRowPayload$json = const {
   '1': 'CreateRowPayload',
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'upper_row_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'upperRowId'},
+    const {'1': 'start_row_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'startRowId'},
   ],
   '8': const [
-    const {'1': 'one_of_upper_row_id'},
+    const {'1': 'one_of_start_row_id'},
   ],
 };
 
 /// Descriptor for `CreateRowPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createRowPayloadDescriptor = $convert.base64Decode('ChBDcmVhdGVSb3dQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIiCgx1cHBlcl9yb3dfaWQYAiABKAlIAFIKdXBwZXJSb3dJZEIVChNvbmVfb2ZfdXBwZXJfcm93X2lk');
+final $typed_data.Uint8List createRowPayloadDescriptor = $convert.base64Decode('ChBDcmVhdGVSb3dQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIiCgxzdGFydF9yb3dfaWQYAiABKAlIAFIKc3RhcnRSb3dJZEIVChNvbmVfb2Zfc3RhcnRfcm93X2lk');
 @$core.Deprecated('Use queryFieldPayloadDescriptor instead')
 const QueryFieldPayload$json = const {
   '1': 'QueryFieldPayload',
@@ -212,9 +232,21 @@ const QueryGridBlocksPayload$json = const {
   '1': 'QueryGridBlocksPayload',
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'blocks', '3': 2, '4': 3, '5': 11, '6': '.GridBlockMeta', '10': 'blocks'},
+    const {'1': 'block_orders', '3': 2, '4': 3, '5': 11, '6': '.GridBlockOrder', '10': 'blockOrders'},
   ],
 };
 
 /// Descriptor for `QueryGridBlocksPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List queryGridBlocksPayloadDescriptor = $convert.base64Decode('ChZRdWVyeUdyaWRCbG9ja3NQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBImCgZibG9ja3MYAiADKAsyDi5HcmlkQmxvY2tNZXRhUgZibG9ja3M=');
+final $typed_data.Uint8List queryGridBlocksPayloadDescriptor = $convert.base64Decode('ChZRdWVyeUdyaWRCbG9ja3NQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIyCgxibG9ja19vcmRlcnMYAiADKAsyDy5HcmlkQmxvY2tPcmRlclILYmxvY2tPcmRlcnM=');
+@$core.Deprecated('Use queryRowPayloadDescriptor instead')
+const QueryRowPayload$json = const {
+  '1': 'QueryRowPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'block_id', '3': 2, '4': 1, '5': 9, '10': 'blockId'},
+    const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'},
+  ],
+};
+
+/// Descriptor for `QueryRowPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List queryRowPayloadDescriptor = $convert.base64Decode('Cg9RdWVyeVJvd1BheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhkKCGJsb2NrX2lkGAIgASgJUgdibG9ja0lkEhUKBnJvd19pZBgDIAEoCVIFcm93SWQ=');

+ 36 - 36
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart

@@ -17,7 +17,7 @@ class GridMeta extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridMeta', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
     ..pc<FieldMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fields', $pb.PbFieldType.PM, subBuilder: FieldMeta.create)
-    ..pc<GridBlockMeta>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: GridBlockMeta.create)
+    ..pc<GridBlockMeta>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockMetas', $pb.PbFieldType.PM, subBuilder: GridBlockMeta.create)
     ..hasRequiredFields = false
   ;
 
@@ -25,7 +25,7 @@ class GridMeta extends $pb.GeneratedMessage {
   factory GridMeta({
     $core.String? gridId,
     $core.Iterable<FieldMeta>? fields,
-    $core.Iterable<GridBlockMeta>? blocks,
+    $core.Iterable<GridBlockMeta>? blockMetas,
   }) {
     final _result = create();
     if (gridId != null) {
@@ -34,8 +34,8 @@ class GridMeta extends $pb.GeneratedMessage {
     if (fields != null) {
       _result.fields.addAll(fields);
     }
-    if (blocks != null) {
-      _result.blocks.addAll(blocks);
+    if (blockMetas != null) {
+      _result.blockMetas.addAll(blockMetas);
     }
     return _result;
   }
@@ -73,7 +73,7 @@ class GridMeta extends $pb.GeneratedMessage {
   $core.List<FieldMeta> get fields => $_getList(1);
 
   @$pb.TagNumber(3)
-  $core.List<GridBlockMeta> get blocks => $_getList(2);
+  $core.List<GridBlockMeta> get blockMetas => $_getList(2);
 }
 
 class GridBlockMeta extends $pb.GeneratedMessage {
@@ -151,15 +151,15 @@ class GridBlockMeta extends $pb.GeneratedMessage {
   void clearRowCount() => clearField(3);
 }
 
-class GridBlockMetaData extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockMetaData', createEmptyInstance: create)
+class GridBlockMetaSerde extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockMetaSerde', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
     ..pc<RowMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowMetas', $pb.PbFieldType.PM, subBuilder: RowMeta.create)
     ..hasRequiredFields = false
   ;
 
-  GridBlockMetaData._() : super();
-  factory GridBlockMetaData({
+  GridBlockMetaSerde._() : super();
+  factory GridBlockMetaSerde({
     $core.String? blockId,
     $core.Iterable<RowMeta>? rowMetas,
   }) {
@@ -172,26 +172,26 @@ class GridBlockMetaData extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory GridBlockMetaData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory GridBlockMetaData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory GridBlockMetaSerde.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GridBlockMetaSerde.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  GridBlockMetaData clone() => GridBlockMetaData()..mergeFromMessage(this);
+  GridBlockMetaSerde clone() => GridBlockMetaSerde()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  GridBlockMetaData copyWith(void Function(GridBlockMetaData) updates) => super.copyWith((message) => updates(message as GridBlockMetaData)) as GridBlockMetaData; // ignore: deprecated_member_use
+  GridBlockMetaSerde copyWith(void Function(GridBlockMetaSerde) updates) => super.copyWith((message) => updates(message as GridBlockMetaSerde)) as GridBlockMetaSerde; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static GridBlockMetaData create() => GridBlockMetaData._();
-  GridBlockMetaData createEmptyInstance() => create();
-  static $pb.PbList<GridBlockMetaData> createRepeated() => $pb.PbList<GridBlockMetaData>();
+  static GridBlockMetaSerde create() => GridBlockMetaSerde._();
+  GridBlockMetaSerde createEmptyInstance() => create();
+  static $pb.PbList<GridBlockMetaSerde> createRepeated() => $pb.PbList<GridBlockMetaSerde>();
   @$core.pragma('dart2js:noInline')
-  static GridBlockMetaData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlockMetaData>(create);
-  static GridBlockMetaData? _defaultInstance;
+  static GridBlockMetaSerde getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlockMetaSerde>(create);
+  static GridBlockMetaSerde? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get blockId => $_getSZ(0);
@@ -1020,26 +1020,26 @@ class CellMetaChangeset extends $pb.GeneratedMessage {
 class BuildGridContext extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BuildGridContext', createEmptyInstance: create)
     ..pc<FieldMeta>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldMetas', $pb.PbFieldType.PM, subBuilder: FieldMeta.create)
-    ..aOM<GridBlockMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridBlock', subBuilder: GridBlockMeta.create)
-    ..aOM<GridBlockMetaData>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridBlockMetaData', subBuilder: GridBlockMetaData.create)
+    ..aOM<GridBlockMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockMetas', subBuilder: GridBlockMeta.create)
+    ..aOM<GridBlockMetaSerde>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockMetaData', subBuilder: GridBlockMetaSerde.create)
     ..hasRequiredFields = false
   ;
 
   BuildGridContext._() : super();
   factory BuildGridContext({
     $core.Iterable<FieldMeta>? fieldMetas,
-    GridBlockMeta? gridBlock,
-    GridBlockMetaData? gridBlockMetaData,
+    GridBlockMeta? blockMetas,
+    GridBlockMetaSerde? blockMetaData,
   }) {
     final _result = create();
     if (fieldMetas != null) {
       _result.fieldMetas.addAll(fieldMetas);
     }
-    if (gridBlock != null) {
-      _result.gridBlock = gridBlock;
+    if (blockMetas != null) {
+      _result.blockMetas = blockMetas;
     }
-    if (gridBlockMetaData != null) {
-      _result.gridBlockMetaData = gridBlockMetaData;
+    if (blockMetaData != null) {
+      _result.blockMetaData = blockMetaData;
     }
     return _result;
   }
@@ -1068,25 +1068,25 @@ class BuildGridContext extends $pb.GeneratedMessage {
   $core.List<FieldMeta> get fieldMetas => $_getList(0);
 
   @$pb.TagNumber(2)
-  GridBlockMeta get gridBlock => $_getN(1);
+  GridBlockMeta get blockMetas => $_getN(1);
   @$pb.TagNumber(2)
-  set gridBlock(GridBlockMeta v) { setField(2, v); }
+  set blockMetas(GridBlockMeta v) { setField(2, v); }
   @$pb.TagNumber(2)
-  $core.bool hasGridBlock() => $_has(1);
+  $core.bool hasBlockMetas() => $_has(1);
   @$pb.TagNumber(2)
-  void clearGridBlock() => clearField(2);
+  void clearBlockMetas() => clearField(2);
   @$pb.TagNumber(2)
-  GridBlockMeta ensureGridBlock() => $_ensure(1);
+  GridBlockMeta ensureBlockMetas() => $_ensure(1);
 
   @$pb.TagNumber(3)
-  GridBlockMetaData get gridBlockMetaData => $_getN(2);
+  GridBlockMetaSerde get blockMetaData => $_getN(2);
   @$pb.TagNumber(3)
-  set gridBlockMetaData(GridBlockMetaData v) { setField(3, v); }
+  set blockMetaData(GridBlockMetaSerde v) { setField(3, v); }
   @$pb.TagNumber(3)
-  $core.bool hasGridBlockMetaData() => $_has(2);
+  $core.bool hasBlockMetaData() => $_has(2);
   @$pb.TagNumber(3)
-  void clearGridBlockMetaData() => clearField(3);
+  void clearBlockMetaData() => clearField(3);
   @$pb.TagNumber(3)
-  GridBlockMetaData ensureGridBlockMetaData() => $_ensure(2);
+  GridBlockMetaSerde ensureBlockMetaData() => $_ensure(2);
 }
 

+ 10 - 10
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart

@@ -29,12 +29,12 @@ const GridMeta$json = const {
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
     const {'1': 'fields', '3': 2, '4': 3, '5': 11, '6': '.FieldMeta', '10': 'fields'},
-    const {'1': 'blocks', '3': 3, '4': 3, '5': 11, '6': '.GridBlockMeta', '10': 'blocks'},
+    const {'1': 'block_metas', '3': 3, '4': 3, '5': 11, '6': '.GridBlockMeta', '10': 'blockMetas'},
   ],
 };
 
 /// Descriptor for `GridMeta`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSIgoGZmllbGRzGAIgAygLMgouRmllbGRNZXRhUgZmaWVsZHMSJgoGYmxvY2tzGAMgAygLMg4uR3JpZEJsb2NrTWV0YVIGYmxvY2tz');
+final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSIgoGZmllbGRzGAIgAygLMgouRmllbGRNZXRhUgZmaWVsZHMSLwoLYmxvY2tfbWV0YXMYAyADKAsyDi5HcmlkQmxvY2tNZXRhUgpibG9ja01ldGFz');
 @$core.Deprecated('Use gridBlockMetaDescriptor instead')
 const GridBlockMeta$json = const {
   '1': 'GridBlockMeta',
@@ -47,17 +47,17 @@ const GridBlockMeta$json = const {
 
 /// Descriptor for `GridBlockMeta`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List gridBlockMetaDescriptor = $convert.base64Decode('Cg1HcmlkQmxvY2tNZXRhEhkKCGJsb2NrX2lkGAEgASgJUgdibG9ja0lkEiYKD3N0YXJ0X3Jvd19pbmRleBgCIAEoBVINc3RhcnRSb3dJbmRleBIbCglyb3dfY291bnQYAyABKAVSCHJvd0NvdW50');
-@$core.Deprecated('Use gridBlockMetaDataDescriptor instead')
-const GridBlockMetaData$json = const {
-  '1': 'GridBlockMetaData',
+@$core.Deprecated('Use gridBlockMetaSerdeDescriptor instead')
+const GridBlockMetaSerde$json = const {
+  '1': 'GridBlockMetaSerde',
   '2': const [
     const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
     const {'1': 'row_metas', '3': 2, '4': 3, '5': 11, '6': '.RowMeta', '10': 'rowMetas'},
   ],
 };
 
-/// Descriptor for `GridBlockMetaData`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridBlockMetaDataDescriptor = $convert.base64Decode('ChFHcmlkQmxvY2tNZXRhRGF0YRIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIlCglyb3dfbWV0YXMYAiADKAsyCC5Sb3dNZXRhUghyb3dNZXRhcw==');
+/// Descriptor for `GridBlockMetaSerde`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List gridBlockMetaSerdeDescriptor = $convert.base64Decode('ChJHcmlkQmxvY2tNZXRhU2VyZGUSGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSJQoJcm93X21ldGFzGAIgAygLMgguUm93TWV0YVIIcm93TWV0YXM=');
 @$core.Deprecated('Use fieldMetaDescriptor instead')
 const FieldMeta$json = const {
   '1': 'FieldMeta',
@@ -197,10 +197,10 @@ const BuildGridContext$json = const {
   '1': 'BuildGridContext',
   '2': const [
     const {'1': 'field_metas', '3': 1, '4': 3, '5': 11, '6': '.FieldMeta', '10': 'fieldMetas'},
-    const {'1': 'grid_block', '3': 2, '4': 1, '5': 11, '6': '.GridBlockMeta', '10': 'gridBlock'},
-    const {'1': 'grid_block_meta_data', '3': 3, '4': 1, '5': 11, '6': '.GridBlockMetaData', '10': 'gridBlockMetaData'},
+    const {'1': 'block_metas', '3': 2, '4': 1, '5': 11, '6': '.GridBlockMeta', '10': 'blockMetas'},
+    const {'1': 'block_meta_data', '3': 3, '4': 1, '5': 11, '6': '.GridBlockMetaSerde', '10': 'blockMetaData'},
   ],
 };
 
 /// Descriptor for `BuildGridContext`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List buildGridContextDescriptor = $convert.base64Decode('ChBCdWlsZEdyaWRDb250ZXh0EisKC2ZpZWxkX21ldGFzGAEgAygLMgouRmllbGRNZXRhUgpmaWVsZE1ldGFzEi0KCmdyaWRfYmxvY2sYAiABKAsyDi5HcmlkQmxvY2tNZXRhUglncmlkQmxvY2sSQwoUZ3JpZF9ibG9ja19tZXRhX2RhdGEYAyABKAsyEi5HcmlkQmxvY2tNZXRhRGF0YVIRZ3JpZEJsb2NrTWV0YURhdGE=');
+final $typed_data.Uint8List buildGridContextDescriptor = $convert.base64Decode('ChBCdWlsZEdyaWRDb250ZXh0EisKC2ZpZWxkX21ldGFzGAEgAygLMgouRmllbGRNZXRhUgpmaWVsZE1ldGFzEi8KC2Jsb2NrX21ldGFzGAIgASgLMg4uR3JpZEJsb2NrTWV0YVIKYmxvY2tNZXRhcxI7Cg9ibG9ja19tZXRhX2RhdGEYAyABKAsyEy5HcmlkQmxvY2tNZXRhU2VyZGVSDWJsb2NrTWV0YURhdGE=');

+ 2 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbenum.dart

@@ -12,12 +12,14 @@ import 'package:protobuf/protobuf.dart' as $pb;
 class GridNotification extends $pb.ProtobufEnum {
   static const GridNotification Unknown = GridNotification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
   static const GridNotification GridDidUpdateBlock = GridNotification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateBlock');
+  static const GridNotification GridDidCreateBlock = GridNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidCreateBlock');
   static const GridNotification GridDidUpdateCells = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateCells');
   static const GridNotification GridDidUpdateFields = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateFields');
 
   static const $core.List<GridNotification> values = <GridNotification> [
     Unknown,
     GridDidUpdateBlock,
+    GridDidCreateBlock,
     GridDidUpdateCells,
     GridDidUpdateFields,
   ];

+ 2 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbjson.dart

@@ -14,10 +14,11 @@ const GridNotification$json = const {
   '2': const [
     const {'1': 'Unknown', '2': 0},
     const {'1': 'GridDidUpdateBlock', '2': 10},
+    const {'1': 'GridDidCreateBlock', '2': 11},
     const {'1': 'GridDidUpdateCells', '2': 20},
     const {'1': 'GridDidUpdateFields', '2': 30},
   ],
 };
 
 /// Descriptor for `GridNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABIWChJHcmlkRGlkVXBkYXRlQmxvY2sQChIWChJHcmlkRGlkVXBkYXRlQ2VsbHMQFBIXChNHcmlkRGlkVXBkYXRlRmllbGRzEB4=');
+final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABIWChJHcmlkRGlkVXBkYXRlQmxvY2sQChIWChJHcmlkRGlkQ3JlYXRlQmxvY2sQCxIWChJHcmlkRGlkVXBkYXRlQ2VsbHMQFBIXChNHcmlkRGlkVXBkYXRlRmllbGRzEB4=');

+ 5 - 3
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart

@@ -12,15 +12,17 @@ import 'package:protobuf/protobuf.dart' as $pb;
 class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent GetGridData = GridEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetGridData');
   static const GridEvent GetGridBlocks = GridEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetGridBlocks');
-  static const GridEvent GetFields = GridEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFields');
-  static const GridEvent CreateRow = GridEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
-  static const GridEvent UpdateCell = GridEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell');
+  static const GridEvent GetFields = GridEvent._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFields');
+  static const GridEvent CreateRow = GridEvent._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
+  static const GridEvent GetRow = GridEvent._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRow');
+  static const GridEvent UpdateCell = GridEvent._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell');
 
   static const $core.List<GridEvent> values = <GridEvent> [
     GetGridData,
     GetGridBlocks,
     GetFields,
     CreateRow,
+    GetRow,
     UpdateCell,
   ];
 

+ 5 - 4
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart

@@ -14,11 +14,12 @@ const GridEvent$json = const {
   '2': const [
     const {'1': 'GetGridData', '2': 0},
     const {'1': 'GetGridBlocks', '2': 1},
-    const {'1': 'GetFields', '2': 2},
-    const {'1': 'CreateRow', '2': 3},
-    const {'1': 'UpdateCell', '2': 4},
+    const {'1': 'GetFields', '2': 10},
+    const {'1': 'CreateRow', '2': 11},
+    const {'1': 'GetRow', '2': 12},
+    const {'1': 'UpdateCell', '2': 20},
   ],
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAISDQoJQ3JlYXRlUm93EAMSDgoKVXBkYXRlQ2VsbBAE');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDQoJQ3JlYXRlUm93EAsSCgoGR2V0Um93EAwSDgoKVXBkYXRlQ2VsbBAU');

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

@@ -1088,6 +1088,7 @@ version = "0.1.0"
 dependencies = [
  "bytes",
  "flowy-derive",
+ "flowy-error-code",
  "lib-infra",
  "protobuf",
  "serde",

+ 2 - 2
frontend/rust-lib/dart-ffi/Cargo.toml

@@ -7,8 +7,8 @@ edition = "2018"
 [lib]
 name = "dart_ffi"
 # this value will change depending on the target os
-# default staticlib
-crate-type = ["staticlib"]
+# default cdylib
+crate-type = ["cdylib"]
 
 
 [dependencies]

+ 30 - 11
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -1,9 +1,10 @@
 use crate::manager::GridManager;
 use flowy_error::FlowyError;
 use flowy_grid_data_model::entities::{
-    CellMetaChangeset, CreateRowPayload, Field, Grid, GridId, QueryFieldPayload, QueryGridBlocksPayload, RepeatedField,
-    RepeatedGridBlock, Row,
+    CellMetaChangeset, CreateRowPayload, Field, Grid, GridId, QueryFieldPayload, QueryGridBlocksPayload,
+    QueryRowPayload, RepeatedField, RepeatedGridBlock, RepeatedRow, Row,
 };
+use flowy_grid_data_model::parser::{CreateRowParams, QueryFieldParams, QueryGridBlocksParams, QueryRowParams};
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
 
@@ -23,20 +24,38 @@ pub(crate) async fn get_grid_blocks_handler(
     data: Data<QueryGridBlocksPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RepeatedGridBlock, FlowyError> {
-    let payload: QueryGridBlocksPayload = data.into_inner();
-    let editor = manager.get_grid_editor(&payload.grid_id)?;
-    let repeated_grid_block = editor.get_grid_blocks(Some(payload.blocks)).await?;
+    let params: QueryGridBlocksParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    let block_ids = params
+        .block_orders
+        .into_iter()
+        .map(|block| block.block_id)
+        .collect::<Vec<String>>();
+    let repeated_grid_block = editor.get_blocks(Some(block_ids)).await?;
     data_result(repeated_grid_block)
 }
 
+#[tracing::instrument(level = "debug", skip(data, manager), err)]
+pub(crate) async fn get_row_handler(
+    data: Data<QueryRowPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> DataResult<Row, FlowyError> {
+    let params: QueryRowParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    match editor.get_row(&params.block_id, &params.row_id).await? {
+        None => Err(FlowyError::record_not_found().context("Can not find the row")),
+        Some(row) => data_result(row),
+    }
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_fields_handler(
     data: Data<QueryFieldPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RepeatedField, FlowyError> {
-    let payload: QueryFieldPayload = data.into_inner();
-    let editor = manager.get_grid_editor(&payload.grid_id)?;
-    let field_metas = editor.get_field_metas(Some(payload.field_orders)).await?;
+    let params: QueryFieldParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    let field_metas = editor.get_field_metas(Some(params.field_orders)).await?;
     let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
     data_result(repeated_field)
 }
@@ -46,9 +65,9 @@ pub(crate) async fn create_row_handler(
     data: Data<CreateRowPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let payload: CreateRowPayload = data.into_inner();
-    let editor = manager.get_grid_editor(payload.grid_id.as_ref())?;
-    let _ = editor.create_row(payload.upper_row_id).await?;
+    let params: CreateRowParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
+    let _ = editor.create_row(params.start_row_id).await?;
     Ok(())
 }
 

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

@@ -12,6 +12,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
         .event(GridEvent::GetFields, get_fields_handler)
         .event(GridEvent::CreateRow, create_row_handler)
+        .event(GridEvent::GetRow, get_row_handler)
         .event(GridEvent::UpdateCell, update_cell_handler);
 
     module
@@ -27,11 +28,14 @@ pub enum GridEvent {
     GetGridBlocks = 1,
 
     #[event(input = "QueryFieldPayload", output = "RepeatedField")]
-    GetFields = 2,
+    GetFields = 10,
 
     #[event(input = "CreateRowPayload", output = "Row")]
-    CreateRow = 3,
+    CreateRow = 11,
+
+    #[event(input = "QueryRowPayload", output = "Row")]
+    GetRow = 12,
 
     #[event(input = "CellMetaChangeset")]
-    UpdateCell = 4,
+    UpdateCell = 20,
 }

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

@@ -182,11 +182,11 @@ pub async fn make_grid_view_data(
     grid_manager: Arc<GridManager>,
     build_context: BuildGridContext,
 ) -> FlowyResult<Bytes> {
-    let block_id = build_context.grid_block.block_id.clone();
+    let block_id = build_context.block_metas.block_id.clone();
     let grid_meta = GridMeta {
         grid_id: view_id.to_string(),
         fields: build_context.field_metas,
-        blocks: vec![build_context.grid_block],
+        block_metas: vec![build_context.block_metas],
     };
 
     let grid_meta_delta = make_grid_delta(&grid_meta);
@@ -195,7 +195,7 @@ pub async fn make_grid_view_data(
         Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
     let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
 
-    let grid_block_meta_delta = make_block_meta_delta(&build_context.grid_block_meta_data);
+    let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
     let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
     let repeated_revision: RepeatedRevision =
         Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into();

+ 7 - 3
frontend/rust-lib/flowy-grid/src/protobuf/model/dart_notification.rs

@@ -27,6 +27,7 @@
 pub enum GridNotification {
     Unknown = 0,
     GridDidUpdateBlock = 10,
+    GridDidCreateBlock = 11,
     GridDidUpdateCells = 20,
     GridDidUpdateFields = 30,
 }
@@ -40,6 +41,7 @@ impl ::protobuf::ProtobufEnum for GridNotification {
         match value {
             0 => ::std::option::Option::Some(GridNotification::Unknown),
             10 => ::std::option::Option::Some(GridNotification::GridDidUpdateBlock),
+            11 => ::std::option::Option::Some(GridNotification::GridDidCreateBlock),
             20 => ::std::option::Option::Some(GridNotification::GridDidUpdateCells),
             30 => ::std::option::Option::Some(GridNotification::GridDidUpdateFields),
             _ => ::std::option::Option::None
@@ -50,6 +52,7 @@ impl ::protobuf::ProtobufEnum for GridNotification {
         static values: &'static [GridNotification] = &[
             GridNotification::Unknown,
             GridNotification::GridDidUpdateBlock,
+            GridNotification::GridDidCreateBlock,
             GridNotification::GridDidUpdateCells,
             GridNotification::GridDidUpdateFields,
         ];
@@ -80,9 +83,10 @@ impl ::protobuf::reflect::ProtobufValue for GridNotification {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x17dart_notification.proto*h\n\x10GridNotification\x12\x0b\n\x07Unkno\
-    wn\x10\0\x12\x16\n\x12GridDidUpdateBlock\x10\n\x12\x16\n\x12GridDidUpdat\
-    eCells\x10\x14\x12\x17\n\x13GridDidUpdateFields\x10\x1eb\x06proto3\
+    \n\x17dart_notification.proto*\x80\x01\n\x10GridNotification\x12\x0b\n\
+    \x07Unknown\x10\0\x12\x16\n\x12GridDidUpdateBlock\x10\n\x12\x16\n\x12Gri\
+    dDidCreateBlock\x10\x0b\x12\x16\n\x12GridDidUpdateCells\x10\x14\x12\x17\
+    \n\x13GridDidUpdateFields\x10\x1eb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 13 - 9
frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs

@@ -27,9 +27,10 @@
 pub enum GridEvent {
     GetGridData = 0,
     GetGridBlocks = 1,
-    GetFields = 2,
-    CreateRow = 3,
-    UpdateCell = 4,
+    GetFields = 10,
+    CreateRow = 11,
+    GetRow = 12,
+    UpdateCell = 20,
 }
 
 impl ::protobuf::ProtobufEnum for GridEvent {
@@ -41,9 +42,10 @@ impl ::protobuf::ProtobufEnum for GridEvent {
         match value {
             0 => ::std::option::Option::Some(GridEvent::GetGridData),
             1 => ::std::option::Option::Some(GridEvent::GetGridBlocks),
-            2 => ::std::option::Option::Some(GridEvent::GetFields),
-            3 => ::std::option::Option::Some(GridEvent::CreateRow),
-            4 => ::std::option::Option::Some(GridEvent::UpdateCell),
+            10 => ::std::option::Option::Some(GridEvent::GetFields),
+            11 => ::std::option::Option::Some(GridEvent::CreateRow),
+            12 => ::std::option::Option::Some(GridEvent::GetRow),
+            20 => ::std::option::Option::Some(GridEvent::UpdateCell),
             _ => ::std::option::Option::None
         }
     }
@@ -54,6 +56,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::GetGridBlocks,
             GridEvent::GetFields,
             GridEvent::CreateRow,
+            GridEvent::GetRow,
             GridEvent::UpdateCell,
         ];
         values
@@ -83,9 +86,10 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*]\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\0\x12\
-    \x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\x02\x12\r\n\tCreate\
-    Row\x10\x03\x12\x0e\n\nUpdateCell\x10\x04b\x06proto3\
+    \n\x0fevent_map.proto*i\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\0\x12\
+    \x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\r\n\tCreateRo\
+    w\x10\x0b\x12\n\n\x06GetRow\x10\x0c\x12\x0e\n\nUpdateCell\x10\x14b\x06pr\
+    oto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -3,6 +3,7 @@ syntax = "proto3";
 enum GridNotification {
     Unknown = 0;
     GridDidUpdateBlock = 10;
+    GridDidCreateBlock = 11;
     GridDidUpdateCells = 20;
     GridDidUpdateFields = 30;
 }

+ 4 - 3
frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto

@@ -3,7 +3,8 @@ syntax = "proto3";
 enum GridEvent {
     GetGridData = 0;
     GetGridBlocks = 1;
-    GetFields = 2;
-    CreateRow = 3;
-    UpdateCell = 4;
+    GetFields = 10;
+    CreateRow = 11;
+    GetRow = 12;
+    UpdateCell = 20;
 }

+ 36 - 34
frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs

@@ -1,14 +1,15 @@
 use crate::manager::GridUser;
-use crate::services::row::{make_cell, make_grid_blocks, make_row_ids_per_block, GridBlockMetaDataSnapshot};
+use crate::services::row::{make_cell, make_row_ids_per_block, GridBlockMetaData};
 use bytes::Bytes;
 
+use crate::dart_notification::{send_dart_notification, GridNotification};
 use dashmap::DashMap;
-use flowy_collaboration::client_grid::{GridBlockMetaDataChange, GridBlockMetaDataPad};
+use flowy_collaboration::client_grid::{GridBlockMetaChange, GridBlockMetaPad};
 use flowy_collaboration::entities::revision::Revision;
 use flowy_collaboration::util::make_delta_from_revisions;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{
-    Cell, FieldMeta, GridBlockId, GridBlockMeta, GridBlockMetaChangeset, RepeatedCell, RepeatedRowOrder, RowMeta,
+    FieldMeta, GridBlockId, GridBlockMeta, GridBlockMetaChangeset, RepeatedCell, RepeatedRowOrder, RowMeta,
     RowMetaChangeset, RowOrder,
 };
 use flowy_sync::disk::SQLiteGridBlockMetaRevisionPersistence;
@@ -17,10 +18,7 @@ use flowy_sync::{
 };
 use lib_infra::future::FutureResult;
 use lib_ot::core::PlainTextAttributes;
-
 use std::collections::HashMap;
-
-use crate::dart_notification::{send_dart_notification, GridNotification};
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
@@ -30,7 +28,7 @@ type BlockId = String;
 pub(crate) struct GridBlockMetaEditorManager {
     grid_id: String,
     user: Arc<dyn GridUser>,
-    editor_map: DashMap<String, Arc<ClientGridBlockMetaDataEditor>>,
+    editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
     block_id_by_row_id: DashMap<BlockId, RowId>,
 }
 
@@ -49,7 +47,7 @@ impl GridBlockMetaEditorManager {
         Ok(manager)
     }
 
-    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaDataEditor>> {
+    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
         match self.editor_map.get(block_id) {
             None => {
                 tracing::error!("The is a fatal error, block is not exist");
@@ -124,6 +122,16 @@ impl GridBlockMetaEditorManager {
         Ok(())
     }
 
+    pub async fn get_row(&self, block_id: &str, row_id: &str) -> FlowyResult<Option<Arc<RowMeta>>> {
+        let editor = self.get_editor(block_id).await?;
+        let mut row_metas = editor.get_row_metas(Some(vec![row_id.to_owned()])).await?;
+        if row_metas.is_empty() {
+            Ok(None)
+        } else {
+            Ok(row_metas.pop())
+        }
+    }
+
     pub async fn update_cells(&self, field_metas: &[FieldMeta], changeset: RowMetaChangeset) -> FlowyResult<()> {
         let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
         let _ = editor.update_row(changeset.clone()).await?;
@@ -131,10 +139,10 @@ impl GridBlockMetaEditorManager {
         Ok(())
     }
 
-    pub(crate) async fn get_block_meta_snapshot_from_blocks(
+    pub(crate) async fn get_block_meta_data_from_blocks(
         &self,
         grid_blocks: Vec<GridBlockMeta>,
-    ) -> FlowyResult<Vec<GridBlockMetaDataSnapshot>> {
+    ) -> FlowyResult<Vec<GridBlockMetaData>> {
         let mut snapshots = vec![];
         for grid_block in grid_blocks {
             let editor = self.get_editor(&grid_block.block_id).await?;
@@ -144,7 +152,7 @@ impl GridBlockMetaEditorManager {
                     .insert(row_meta.id.clone(), row_meta.block_id.clone());
             });
 
-            snapshots.push(GridBlockMetaDataSnapshot {
+            snapshots.push(GridBlockMetaData {
                 block_id: grid_block.block_id,
                 row_metas,
             });
@@ -152,20 +160,17 @@ impl GridBlockMetaEditorManager {
         Ok(snapshots)
     }
 
-    pub(crate) async fn get_block_meta_snapshot_from_row_orders(
-        &self,
-        grid_block_metas: &Vec<GridBlockMeta>,
-    ) -> FlowyResult<Vec<GridBlockMetaDataSnapshot>> {
+    pub(crate) async fn get_block_meta_data(&self, block_ids: &[String]) -> FlowyResult<Vec<GridBlockMetaData>> {
         let mut snapshots = vec![];
-        for grid_block_meta in grid_block_metas {
-            let editor = self.get_editor(&grid_block_meta.block_id).await?;
+        for block_id in block_ids {
+            let editor = self.get_editor(&block_id).await?;
             let row_metas = editor.get_row_metas(None).await?;
             row_metas.iter().for_each(|row_meta| {
                 self.block_id_by_row_id
                     .insert(row_meta.id.clone(), row_meta.block_id.clone());
             });
-            snapshots.push(GridBlockMetaDataSnapshot {
-                block_id: grid_block_meta.block_id.clone(),
+            snapshots.push(GridBlockMetaData {
+                block_id: block_id.clone(),
                 row_metas,
             });
         }
@@ -182,7 +187,7 @@ impl GridBlockMetaEditorManager {
         Ok(row_orders)
     }
 
-    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaDataEditor>> {
+    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
         match self.block_id_by_row_id.get(row_id) {
             None => {
                 let msg = format!(
@@ -237,7 +242,7 @@ impl GridBlockMetaEditorManager {
 async fn make_block_meta_editor_map(
     user: &Arc<dyn GridUser>,
     blocks: Vec<GridBlockMeta>,
-) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaDataEditor>>> {
+) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
     let editor_map = DashMap::new();
     for block in blocks {
         let editor = make_block_meta_editor(user, &block.block_id).await?;
@@ -247,10 +252,7 @@ async fn make_block_meta_editor_map(
     Ok(editor_map)
 }
 
-async fn make_block_meta_editor(
-    user: &Arc<dyn GridUser>,
-    block_id: &str,
-) -> FlowyResult<ClientGridBlockMetaDataEditor> {
+async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
     let token = user.token()?;
     let user_id = user.user_id()?;
     let pool = user.db_pool()?;
@@ -258,17 +260,17 @@ async fn make_block_meta_editor(
     let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
     let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
     let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
-    ClientGridBlockMetaDataEditor::new(&user_id, &token, block_id, rev_manager).await
+    ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
 }
 
-pub struct ClientGridBlockMetaDataEditor {
+pub struct ClientGridBlockMetaEditor {
     user_id: String,
     pub block_id: String,
-    pad: Arc<RwLock<GridBlockMetaDataPad>>,
+    pad: Arc<RwLock<GridBlockMetaPad>>,
     rev_manager: Arc<RevisionManager>,
 }
 
-impl ClientGridBlockMetaDataEditor {
+impl ClientGridBlockMetaEditor {
     pub async fn new(
         user_id: &str,
         token: &str,
@@ -340,7 +342,7 @@ impl ClientGridBlockMetaDataEditor {
 
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
-        F: for<'a> FnOnce(&'a mut GridBlockMetaDataPad) -> FlowyResult<Option<GridBlockMetaDataChange>>,
+        F: for<'a> FnOnce(&'a mut GridBlockMetaPad) -> FlowyResult<Option<GridBlockMetaChange>>,
     {
         let mut write_guard = self.pad.write().await;
         match f(&mut *write_guard)? {
@@ -352,8 +354,8 @@ impl ClientGridBlockMetaDataEditor {
         Ok(())
     }
 
-    async fn apply_change(&self, change: GridBlockMetaDataChange) -> FlowyResult<()> {
-        let GridBlockMetaDataChange { delta, md5 } = change;
+    async fn apply_change(&self, change: GridBlockMetaChange) -> FlowyResult<()> {
+        let GridBlockMetaChange { delta, md5 } = change;
         let user_id = self.user_id.clone();
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
         let delta_data = delta.to_delta_bytes();
@@ -387,10 +389,10 @@ impl RevisionCloudService for GridBlockMetaRevisionCloudService {
 
 struct GridBlockMetaPadBuilder();
 impl RevisionObjectBuilder for GridBlockMetaPadBuilder {
-    type Output = GridBlockMetaDataPad;
+    type Output = GridBlockMetaPad;
 
     fn build_object(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
-        let pad = GridBlockMetaDataPad::from_revisions(object_id, revisions)?;
+        let pad = GridBlockMetaPad::from_revisions(object_id, revisions)?;
         Ok(pad)
     }
 }

+ 71 - 40
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -7,14 +7,15 @@ use flowy_collaboration::util::make_delta_from_revisions;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{
     Cell, CellMetaChangeset, Field, FieldChangeset, FieldMeta, Grid, GridBlockMeta, GridBlockMetaChangeset,
-    RepeatedField, RepeatedFieldOrder, RepeatedGridBlock, RepeatedRowOrder, Row, RowMeta, RowMetaChangeset,
+    GridBlockOrder, RepeatedField, RepeatedFieldOrder, RepeatedGridBlock, RepeatedRow, RepeatedRowOrder, Row, RowMeta,
+    RowMetaChangeset,
 };
 use std::collections::HashMap;
 
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::services::row::{
-    make_grid_block_from_block_metas, make_grid_blocks, make_row_ids_per_block, row_meta_from_context,
-    serialize_cell_data, GridBlockMetaDataSnapshot, RowMetaContext, RowMetaContextBuilder,
+    make_grid_block_from_block_metas, make_grid_blocks, make_row_ids_per_block, make_rows_from_row_metas,
+    row_meta_from_context, serialize_cell_data, GridBlockMetaData, RowMetaContext, RowMetaContextBuilder,
 };
 use flowy_sync::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
 use lib_infra::future::FutureResult;
@@ -125,6 +126,33 @@ impl ClientGridEditor {
         self.block_meta_manager.update_row(changeset).await
     }
 
+    pub async fn get_rows(&self, block_id: &str) -> FlowyResult<RepeatedRow> {
+        let block_ids = vec![block_id.to_owned()];
+        let mut block_meta_data_vec = self.get_block_meta_data_vec(Some(&block_ids)).await?;
+        debug_assert_eq!(block_meta_data_vec.len(), 1);
+        if block_meta_data_vec.len() == 1 {
+            let block_meta_data = block_meta_data_vec.pop().unwrap();
+            let field_metas = self.get_field_metas(None).await?;
+            let rows = make_rows_from_row_metas(&field_metas, &block_meta_data.row_metas);
+            Ok(rows.into())
+        } else {
+            Ok(vec![].into())
+        }
+    }
+
+    pub async fn get_row(&self, block_id: &str, row_id: &str) -> FlowyResult<Option<Row>> {
+        match self.block_meta_manager.get_row(block_id, row_id).await? {
+            None => Ok(None),
+            Some(row) => {
+                let field_metas = self.get_field_metas(None).await?;
+                let row_metas = vec![row];
+                let mut rows = make_rows_from_row_metas(&field_metas, &row_metas);
+                debug_assert!(rows.len() == 1);
+                Ok(rows.pop())
+            }
+        }
+    }
+
     pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
         if let Some(cell_data) = changeset.data.as_ref() {
             match self.pad.read().await.get_field(&changeset.field_id) {
@@ -147,41 +175,17 @@ impl ClientGridEditor {
         Ok(())
     }
 
-    pub async fn get_grid_blocks(
-        &self,
-        grid_block_metas: Option<Vec<GridBlockMeta>>,
-    ) -> FlowyResult<RepeatedGridBlock> {
-        let grid_block_meta_snapshots = self.get_grid_block_meta_snapshots(grid_block_metas.as_ref()).await?;
-        let field_meta = self.pad.read().await.get_field_metas(None)?;
-        match grid_block_metas {
-            None => make_grid_blocks(&field_meta, grid_block_meta_snapshots),
-            Some(grid_block_metas) => {
-                make_grid_block_from_block_metas(&field_meta, grid_block_metas, grid_block_meta_snapshots)
-            }
+    pub async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<RepeatedGridBlock> {
+        let block_meta_data_vec = self.get_block_meta_data_vec(block_ids.as_ref()).await?;
+        match block_ids {
+            None => make_grid_blocks(block_meta_data_vec),
+            Some(block_ids) => make_grid_block_from_block_metas(&block_ids, block_meta_data_vec),
         }
     }
 
-    pub(crate) async fn get_grid_block_meta_snapshots(
-        &self,
-        grid_block_infos: Option<&Vec<GridBlockMeta>>,
-    ) -> FlowyResult<Vec<GridBlockMetaDataSnapshot>> {
-        match grid_block_infos {
-            None => {
-                let grid_blocks = self.pad.read().await.get_blocks();
-                let row_metas_per_block = self
-                    .block_meta_manager
-                    .get_block_meta_snapshot_from_blocks(grid_blocks)
-                    .await?;
-                Ok(row_metas_per_block)
-            }
-            Some(grid_block_infos) => {
-                let row_metas_per_block = self
-                    .block_meta_manager
-                    .get_block_meta_snapshot_from_row_orders(grid_block_infos)
-                    .await?;
-                Ok(row_metas_per_block)
-            }
-        }
+    pub async fn get_block_metas(&self) -> FlowyResult<Vec<GridBlockMeta>> {
+        let grid_blocks = self.pad.read().await.get_blocks();
+        Ok(grid_blocks)
     }
 
     pub async fn delete_rows(&self, row_ids: Vec<String>) -> FlowyResult<()> {
@@ -194,11 +198,20 @@ impl ClientGridEditor {
 
     pub async fn grid_data(&self) -> FlowyResult<Grid> {
         let field_orders = self.pad.read().await.get_field_orders();
-        let block_orders = self.pad.read().await.get_blocks();
+        let block_orders = self
+            .pad
+            .read()
+            .await
+            .get_blocks()
+            .into_iter()
+            .map(|grid_block_meta| GridBlockOrder {
+                block_id: grid_block_meta.block_id,
+            })
+            .collect::<Vec<_>>();
         Ok(Grid {
             id: self.grid_id.clone(),
             field_orders,
-            blocks: block_orders,
+            block_orders,
         })
     }
 
@@ -207,9 +220,27 @@ impl ClientGridEditor {
         Ok(field_meta)
     }
 
-    pub async fn get_blocks(&self) -> FlowyResult<Vec<GridBlockMeta>> {
-        let grid_blocks = self.pad.read().await.get_blocks();
-        Ok(grid_blocks)
+    pub async fn get_block_meta_data_vec(
+        &self,
+        block_ids: Option<&Vec<String>>,
+    ) -> FlowyResult<Vec<GridBlockMetaData>> {
+        match block_ids {
+            None => {
+                let grid_blocks = self.pad.read().await.get_blocks();
+                let row_metas_per_block = self
+                    .block_meta_manager
+                    .get_block_meta_data_from_blocks(grid_blocks)
+                    .await?;
+                Ok(row_metas_per_block)
+            }
+            Some(block_ids) => {
+                let row_metas_per_block = self
+                    .block_meta_manager
+                    .get_block_meta_data(block_ids.as_slice())
+                    .await?;
+                Ok(row_metas_per_block)
+            }
+        }
     }
 
     pub async fn delta_bytes(&self) -> Bytes {

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

@@ -1,11 +1,11 @@
 use crate::services::row::deserialize_cell_data;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::entities::{
-    Cell, CellMeta, FieldMeta, GridBlock, GridBlockMeta, RepeatedGridBlock, RepeatedRowOrder, Row, RowMeta, RowOrder,
+    Cell, CellMeta, FieldMeta, GridBlock, RepeatedGridBlock, RepeatedRowOrder, Row, RowMeta, RowOrder,
 };
 use rayon::iter::{IntoParallelIterator, ParallelIterator};
 use std::collections::HashMap;
-use std::ops::Deref;
+
 use std::sync::Arc;
 
 pub(crate) struct RowIdsPerBlock {
@@ -22,9 +22,9 @@ impl RowIdsPerBlock {
     }
 }
 
-pub(crate) struct GridBlockMetaDataSnapshot {
+pub struct GridBlockMetaData {
     pub(crate) block_id: String,
-    pub(crate) row_metas: Vec<Arc<RowMeta>>,
+    pub row_metas: Vec<Arc<RowMeta>>,
 }
 
 pub(crate) fn make_row_ids_per_block(row_orders: &[RowOrder]) -> Vec<RowIdsPerBlock> {
@@ -40,17 +40,14 @@ pub(crate) fn make_row_ids_per_block(row_orders: &[RowOrder]) -> Vec<RowIdsPerBl
     map.into_values().collect::<Vec<_>>()
 }
 
-pub(crate) fn make_grid_blocks(
-    field_metas: &[FieldMeta],
-    grid_block_meta_snapshots: Vec<GridBlockMetaDataSnapshot>,
-) -> FlowyResult<RepeatedGridBlock> {
-    Ok(grid_block_meta_snapshots
+pub(crate) fn make_grid_blocks(block_meta_snapshots: Vec<GridBlockMetaData>) -> FlowyResult<RepeatedGridBlock> {
+    Ok(block_meta_snapshots
         .into_iter()
         .map(|row_metas_per_block| {
-            let rows = make_rows_from_row_metas(field_metas, &row_metas_per_block.row_metas);
+            let row_ids = make_row_ids_from_row_metas(&row_metas_per_block.row_metas);
             GridBlock {
                 block_id: row_metas_per_block.block_id,
-                rows,
+                row_ids,
             }
         })
         .collect::<Vec<GridBlock>>()
@@ -76,6 +73,10 @@ pub fn make_cell(
     }
 }
 
+pub(crate) fn make_row_ids_from_row_metas(row_metas: &Vec<Arc<RowMeta>>) -> Vec<String> {
+    row_metas.iter().map(|row_meta| row_meta.id.clone()).collect::<Vec<_>>()
+}
+
 pub(crate) fn make_rows_from_row_metas(fields: &[FieldMeta], row_metas: &Vec<Arc<RowMeta>>) -> Vec<Row> {
     let field_meta_map = fields
         .iter()
@@ -101,22 +102,21 @@ pub(crate) fn make_rows_from_row_metas(fields: &[FieldMeta], row_metas: &Vec<Arc
 }
 
 pub(crate) fn make_grid_block_from_block_metas(
-    field_metas: &[FieldMeta],
-    grid_block_metas: Vec<GridBlockMeta>,
-    grid_block_meta_snapshots: Vec<GridBlockMetaDataSnapshot>,
+    block_ids: &[String],
+    block_meta_data_vec: Vec<GridBlockMetaData>,
 ) -> FlowyResult<RepeatedGridBlock> {
-    let block_meta_snapshot_map: HashMap<&String, &Vec<Arc<RowMeta>>> = grid_block_meta_snapshots
+    let block_meta_data_map: HashMap<&String, &Vec<Arc<RowMeta>>> = block_meta_data_vec
         .iter()
-        .map(|snapshot| (&snapshot.block_id, &snapshot.row_metas))
+        .map(|data| (&data.block_id, &data.row_metas))
         .collect();
 
     let mut grid_blocks = vec![];
-    for grid_block_meta in grid_block_metas {
-        match block_meta_snapshot_map.get(&grid_block_meta.block_id) {
+    for block_id in block_ids {
+        match block_meta_data_map.get(&block_id) {
             None => {}
             Some(row_metas) => {
-                let rows = make_rows_from_row_metas(&field_metas, row_metas);
-                grid_blocks.push(GridBlock::new(&grid_block_meta.block_id, rows));
+                let row_ids = make_row_ids_from_row_metas(row_metas);
+                grid_blocks.push(GridBlock::new(block_id, row_ids));
             }
         }
     }

+ 27 - 13
frontend/rust-lib/flowy-grid/tests/grid/script.rs

@@ -89,8 +89,8 @@ impl GridEditorTest {
         let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
         let field_metas = editor.get_field_metas(None).await.unwrap();
-        let grid_blocks = editor.get_blocks().await.unwrap();
-        let row_metas = editor.get_grid_block_meta_snapshots(None).await.unwrap();
+        let grid_blocks = editor.get_block_metas().await.unwrap();
+        let row_metas = get_row_metas(&editor).await;
 
         let grid_id = test.view.id;
         Self {
@@ -150,13 +150,13 @@ impl GridEditorTest {
             }
             EditorScript::CreateBlock { block } => {
                 self.editor.create_block(block).await.unwrap();
-                self.grid_blocks = self.editor.get_blocks().await.unwrap();
+                self.grid_blocks = self.editor.get_block_metas().await.unwrap();
             }
             EditorScript::UpdateBlock { changeset: change } => {
                 self.editor.update_block(change).await.unwrap();
             }
             EditorScript::AssertBlockCount(count) => {
-                assert_eq!(self.editor.get_blocks().await.unwrap().len(), count);
+                assert_eq!(self.editor.get_block_metas().await.unwrap().len(), count);
             }
             EditorScript::AssertBlock {
                 block_index,
@@ -167,25 +167,25 @@ impl GridEditorTest {
                 assert_eq!(self.grid_blocks[block_index].start_row_index, start_row_index);
             }
             EditorScript::AssertBlockEqual { block_index, block } => {
-                let blocks = self.editor.get_blocks().await.unwrap();
+                let blocks = self.editor.get_block_metas().await.unwrap();
                 let compared_block = blocks[block_index].clone();
                 assert_eq!(compared_block, block);
             }
             EditorScript::CreateEmptyRow => {
                 self.editor.create_row(None).await.unwrap();
-                self.row_metas = self.editor.get_grid_block_meta_snapshots(None).await.unwrap();
-                self.grid_blocks = self.editor.get_blocks().await.unwrap();
+                self.row_metas = self.get_row_metas().await;
+                self.grid_blocks = self.editor.get_block_metas().await.unwrap();
             }
             EditorScript::CreateRow { context } => {
                 self.editor.insert_rows(vec![context]).await.unwrap();
-                self.row_metas = self.editor.get_grid_block_meta_snapshots(None).await.unwrap();
-                self.grid_blocks = self.editor.get_blocks().await.unwrap();
+                self.row_metas = self.get_row_metas().await;
+                self.grid_blocks = self.editor.get_block_metas().await.unwrap();
             }
             EditorScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
             EditorScript::DeleteRow { row_ids } => {
                 self.editor.delete_rows(row_ids).await.unwrap();
-                self.row_metas = self.editor.get_grid_block_meta_snapshots(None).await.unwrap();
-                self.grid_blocks = self.editor.get_blocks().await.unwrap();
+                self.row_metas = self.get_row_metas().await;
+                self.grid_blocks = self.editor.get_block_metas().await.unwrap();
             }
             EditorScript::AssertRow { changeset } => {
                 let row = self.row_metas.iter().find(|row| row.id == changeset.row_id).unwrap();
@@ -204,11 +204,11 @@ impl GridEditorTest {
                     assert!(result.is_err())
                 } else {
                     let _ = result.unwrap();
-                    self.row_metas = self.editor.get_grid_block_meta_snapshots(None).await.unwrap();
+                    self.row_metas = self.get_row_metas().await;
                 }
             }
             EditorScript::AssertRowCount(count) => {
-                assert_eq!(self.editor.get_grid_blocks(None).await.unwrap().len(), count);
+                assert_eq!(self.row_metas.len(), count);
             }
             EditorScript::AssertGridMetaPad => {
                 sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
@@ -218,6 +218,20 @@ impl GridEditorTest {
             }
         }
     }
+
+    async fn get_row_metas(&self) -> Vec<Arc<RowMeta>> {
+        get_row_metas(&self.editor).await
+    }
+}
+
+async fn get_row_metas(editor: &Arc<ClientGridEditor>) -> Vec<Arc<RowMeta>> {
+    editor
+        .get_block_meta_data_vec(None)
+        .await
+        .unwrap()
+        .pop()
+        .unwrap()
+        .row_metas
 }
 
 pub fn create_text_field() -> FieldMeta {

+ 1 - 0
shared-lib/Cargo.lock

@@ -485,6 +485,7 @@ version = "0.1.0"
 dependencies = [
  "bytes",
  "flowy-derive",
+ "flowy-error-code",
  "lib-infra",
  "protobuf",
  "serde",

+ 53 - 45
shared-lib/flowy-collaboration/src/client_grid/grid_block_meta_data_pad.rs → shared-lib/flowy-collaboration/src/client_grid/grid_block_meta_pad.rs

@@ -1,7 +1,7 @@
 use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
-use flowy_grid_data_model::entities::{GridBlockMetaData, RowMeta, RowMetaChangeset};
+use flowy_grid_data_model::entities::{GridBlockMetaSerde, RowMeta, RowMetaChangeset};
 use lib_infra::uuid;
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
 use serde::{Deserialize, Serialize};
@@ -12,19 +12,22 @@ pub type GridBlockMetaDelta = PlainTextDelta;
 pub type GridBlockMetaDeltaBuilder = PlainTextDeltaBuilder;
 
 #[derive(Debug, Deserialize, Serialize, Clone)]
-pub struct GridBlockMetaDataPad {
+pub struct GridBlockMetaPad {
     block_id: String,
-    rows: Vec<Arc<RowMeta>>,
+    row_metas: Vec<Arc<RowMeta>>,
 
     #[serde(skip)]
     pub(crate) delta: GridBlockMetaDelta,
 }
 
-impl GridBlockMetaDataPad {
+impl GridBlockMetaPad {
     pub fn from_delta(delta: GridBlockMetaDelta) -> CollaborateResult<Self> {
         let s = delta.to_str()?;
-        let block_meta: GridBlockMetaData = serde_json::from_str(&s).map_err(|e| {
-            CollaborateError::internal().context(format!("Deserialize delta to block meta failed: {}", e))
+        tracing::info!("delta: {}", delta);
+        tracing::info!("{}", s);
+        let block_meta: GridBlockMetaSerde = serde_json::from_str(&s).map_err(|e| {
+            let msg = format!("Deserialize delta to block meta failed: {}", e);
+            CollaborateError::internal().context(msg)
         })?;
         let block_id = block_meta.block_id;
         let rows = block_meta
@@ -32,7 +35,11 @@ impl GridBlockMetaDataPad {
             .into_iter()
             .map(Arc::new)
             .collect::<Vec<Arc<RowMeta>>>();
-        Ok(Self { block_id, rows, delta })
+        Ok(Self {
+            block_id,
+            row_metas: rows,
+            delta,
+        })
     }
 
     pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
@@ -44,7 +51,7 @@ impl GridBlockMetaDataPad {
         &mut self,
         row: RowMeta,
         start_row_id: Option<String>,
-    ) -> CollaborateResult<Option<GridBlockMetaDataChange>> {
+    ) -> CollaborateResult<Option<GridBlockMetaChange>> {
         self.modify(|rows| {
             if let Some(upper_row_id) = start_row_id {
                 if upper_row_id.is_empty() {
@@ -63,7 +70,7 @@ impl GridBlockMetaDataPad {
         })
     }
 
-    pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult<Option<GridBlockMetaDataChange>> {
+    pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult<Option<GridBlockMetaChange>> {
         self.modify(|rows| {
             rows.retain(|row| !row_ids.contains(&row.id));
             Ok(Some(()))
@@ -72,10 +79,10 @@ impl GridBlockMetaDataPad {
 
     pub fn get_rows(&self, row_ids: Option<Vec<String>>) -> CollaborateResult<Vec<Arc<RowMeta>>> {
         match row_ids {
-            None => Ok(self.rows.to_vec()),
+            None => Ok(self.row_metas.to_vec()),
             Some(row_ids) => {
                 let row_map = self
-                    .rows
+                    .row_metas
                     .iter()
                     .map(|row| (&row.id, row.clone()))
                     .collect::<HashMap<&String, Arc<RowMeta>>>();
@@ -95,10 +102,10 @@ impl GridBlockMetaDataPad {
     }
 
     pub fn number_of_rows(&self) -> i32 {
-        self.rows.len() as i32
+        self.row_metas.len() as i32
     }
 
-    pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockMetaDataChange>> {
+    pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockMetaChange>> {
         let row_id = changeset.row_id.clone();
         self.modify_row(&row_id, |row| {
             let mut is_changed = None;
@@ -123,12 +130,12 @@ impl GridBlockMetaDataPad {
         })
     }
 
-    pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaDataChange>>
+    pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
     where
         F: for<'a> FnOnce(&'a mut Vec<Arc<RowMeta>>) -> CollaborateResult<Option<()>>,
     {
         let cloned_self = self.clone();
-        match f(&mut self.rows)? {
+        match f(&mut self.row_metas)? {
             None => Ok(None),
             Some(_) => {
                 let old = cloned_self.to_json()?;
@@ -137,14 +144,14 @@ impl GridBlockMetaDataPad {
                     None => Ok(None),
                     Some(delta) => {
                         self.delta = self.delta.compose(&delta)?;
-                        Ok(Some(GridBlockMetaDataChange { delta, md5: self.md5() }))
+                        Ok(Some(GridBlockMetaChange { delta, md5: self.md5() }))
                     }
                 }
             }
         }
     }
 
-    fn modify_row<F>(&mut self, row_id: &str, f: F) -> CollaborateResult<Option<GridBlockMetaDataChange>>
+    fn modify_row<F>(&mut self, row_id: &str, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
     where
         F: FnOnce(&mut RowMeta) -> CollaborateResult<Option<()>>,
     {
@@ -172,35 +179,35 @@ impl GridBlockMetaDataPad {
     }
 }
 
-pub struct GridBlockMetaDataChange {
+pub struct GridBlockMetaChange {
     pub delta: GridBlockMetaDelta,
     /// md5: the md5 of the grid after applying the change.
     pub md5: String,
 }
 
-pub fn make_block_meta_delta(grid_block_meta_data: &GridBlockMetaData) -> GridBlockMetaDelta {
+pub fn make_block_meta_delta(grid_block_meta_data: &GridBlockMetaSerde) -> GridBlockMetaDelta {
     let json = serde_json::to_string(&grid_block_meta_data).unwrap();
     PlainTextDeltaBuilder::new().insert(&json).build()
 }
 
-pub fn make_block_meta_revisions(user_id: &str, grid_block_meta_data: &GridBlockMetaData) -> RepeatedRevision {
+pub fn make_block_meta_revisions(user_id: &str, grid_block_meta_data: &GridBlockMetaSerde) -> RepeatedRevision {
     let delta = make_block_meta_delta(grid_block_meta_data);
     let bytes = delta.to_delta_bytes();
     let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
     revision.into()
 }
 
-impl std::default::Default for GridBlockMetaDataPad {
+impl std::default::Default for GridBlockMetaPad {
     fn default() -> Self {
-        let block_meta_data = GridBlockMetaData {
+        let block_meta_data = GridBlockMetaSerde {
             block_id: uuid(),
             row_metas: vec![],
         };
 
         let delta = make_block_meta_delta(&block_meta_data);
-        GridBlockMetaDataPad {
+        GridBlockMetaPad {
             block_id: block_meta_data.block_id,
-            rows: block_meta_data.row_metas.into_iter().map(Arc::new).collect::<Vec<_>>(),
+            row_metas: block_meta_data.row_metas.into_iter().map(Arc::new).collect::<Vec<_>>(),
             delta,
         }
     }
@@ -208,7 +215,7 @@ impl std::default::Default for GridBlockMetaDataPad {
 
 #[cfg(test)]
 mod tests {
-    use crate::client_grid::{GridBlockMetaDataPad, GridBlockMetaDelta};
+    use crate::client_grid::{GridBlockMetaDelta, GridBlockMetaPad};
     use flowy_grid_data_model::entities::{RowMeta, RowMetaChangeset};
 
     #[test]
@@ -225,7 +232,7 @@ mod tests {
         let change = pad.add_row(row, None).unwrap().unwrap();
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
+            r#"[{"retain":29},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
     }
 
@@ -239,27 +246,27 @@ mod tests {
         let change = pad.add_row(row_1.clone(), None).unwrap().unwrap();
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
+            r#"[{"retain":29},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
 
         let change = pad.add_row(row_2.clone(), None).unwrap().unwrap();
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":101},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
+            r#"[{"retain":106},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
 
         let change = pad.add_row(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":109},{"insert":"3\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false},{\"id\":\""},{"retain":72}]"#
+            r#"[{"retain":114},{"insert":"3\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false},{\"id\":\""},{"retain":72}]"#
         );
 
-        assert_eq!(*pad.rows[0], row_1);
-        assert_eq!(*pad.rows[1], row_3);
-        assert_eq!(*pad.rows[2], row_2);
+        assert_eq!(*pad.row_metas[0], row_1);
+        assert_eq!(*pad.row_metas[1], row_3);
+        assert_eq!(*pad.row_metas[2], row_2);
     }
 
-    fn test_row_meta(id: &str, pad: &GridBlockMetaDataPad) -> RowMeta {
+    fn test_row_meta(id: &str, pad: &GridBlockMetaPad) -> RowMeta {
         RowMeta {
             id: id.to_string(),
             block_id: pad.block_id.clone(),
@@ -280,9 +287,9 @@ mod tests {
         let _ = pad.add_row(row_2.clone(), None).unwrap().unwrap();
         let _ = pad.add_row(row_3.clone(), Some("1".to_string())).unwrap().unwrap();
 
-        assert_eq!(*pad.rows[0], row_3);
-        assert_eq!(*pad.rows[1], row_1);
-        assert_eq!(*pad.rows[2], row_2);
+        assert_eq!(*pad.row_metas[0], row_3);
+        assert_eq!(*pad.row_metas[1], row_1);
+        assert_eq!(*pad.row_metas[2], row_2);
     }
 
     #[test]
@@ -296,9 +303,9 @@ mod tests {
         let _ = pad.add_row(row_2.clone(), None).unwrap().unwrap();
         let _ = pad.add_row(row_3.clone(), Some("".to_string())).unwrap().unwrap();
 
-        assert_eq!(*pad.rows[0], row_3);
-        assert_eq!(*pad.rows[1], row_1);
-        assert_eq!(*pad.rows[2], row_2);
+        assert_eq!(*pad.row_metas[0], row_3);
+        assert_eq!(*pad.row_metas[1], row_1);
+        assert_eq!(*pad.row_metas[2], row_2);
     }
 
     #[test]
@@ -317,7 +324,7 @@ mod tests {
         let change = pad.delete_rows(&[row.id]).unwrap().unwrap();
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":24},{"delete":77},{"retain":2}]"#
+            r#"[{"retain":29},{"delete":77},{"retain":2}]"#
         );
 
         assert_eq!(pad.delta_str(), pre_delta_str);
@@ -346,17 +353,18 @@ mod tests {
 
         assert_eq!(
             change.delta.to_delta_str(),
-            r#"[{"retain":80},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
+            r#"[{"retain":85},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
         );
 
         assert_eq!(
             pad.to_json().unwrap(),
-            r#"{"block_id":"1","rows":[{"id":"1","block_id":"1","cell_by_field_id":{},"height":100,"visibility":true}]}"#
+            r#"{"block_id":"1","row_metas":[{"id":"1","block_id":"1","cell_by_field_id":{},"height":100,"visibility":true}]}"#
         );
     }
 
-    fn test_pad() -> GridBlockMetaDataPad {
-        let delta = GridBlockMetaDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
-        GridBlockMetaDataPad::from_delta(delta).unwrap()
+    fn test_pad() -> GridBlockMetaPad {
+        let delta =
+            GridBlockMetaDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"row_metas\":[]}"}]"#).unwrap();
+        GridBlockMetaPad::from_delta(delta).unwrap()
     }
 }

+ 7 - 7
shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs

@@ -13,9 +13,9 @@ impl GridBuilder {
     }
 
     pub fn add_empty_row(mut self) -> Self {
-        let row = RowMeta::new(&self.build_context.grid_block.block_id);
-        self.build_context.grid_block_meta_data.row_metas.push(row);
-        self.build_context.grid_block.row_count += 1;
+        let row = RowMeta::new(&self.build_context.block_metas.block_id);
+        self.build_context.block_meta_data.row_metas.push(row);
+        self.build_context.block_metas.row_count += 1;
         self
     }
 
@@ -41,7 +41,7 @@ fn check_rows(fields: &[FieldMeta], rows: &[RowMeta]) -> CollaborateResult<()> {
 mod tests {
 
     use crate::client_grid::{make_block_meta_delta, make_grid_delta, GridBuilder};
-    use flowy_grid_data_model::entities::{FieldMeta, FieldType, GridBlockMetaData, GridMeta};
+    use flowy_grid_data_model::entities::{FieldMeta, FieldType, GridBlockMetaSerde, GridMeta};
 
     #[test]
     fn create_default_grid_test() {
@@ -57,13 +57,13 @@ mod tests {
         let grid_meta = GridMeta {
             grid_id,
             fields: build_context.field_metas,
-            blocks: vec![build_context.grid_block],
+            block_metas: vec![build_context.block_metas],
         };
 
         let grid_meta_delta = make_grid_delta(&grid_meta);
         let _: GridMeta = serde_json::from_str(&grid_meta_delta.to_str().unwrap()).unwrap();
 
-        let grid_block_meta_delta = make_block_meta_delta(&build_context.grid_block_meta_data);
-        let _: GridBlockMetaData = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
+        let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
+        let _: GridBlockMetaSerde = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
     }
 }

+ 8 - 8
shared-lib/flowy-collaboration/src/client_grid/grid_meta_pad.rs

@@ -140,12 +140,12 @@ impl GridMetaPad {
 
     pub fn create_block(&mut self, block: GridBlockMeta) -> CollaborateResult<Option<GridChangeset>> {
         self.modify_grid(|grid| {
-            if grid.blocks.iter().any(|b| b.block_id == block.block_id) {
+            if grid.block_metas.iter().any(|b| b.block_id == block.block_id) {
                 tracing::warn!("Duplicate grid block");
                 Ok(None)
             } else {
-                match grid.blocks.last() {
-                    None => grid.blocks.push(block),
+                match grid.block_metas.last() {
+                    None => grid.block_metas.push(block),
                     Some(last_block) => {
                         if last_block.start_row_index > block.start_row_index
                             && last_block.len() > block.start_row_index
@@ -153,7 +153,7 @@ impl GridMetaPad {
                             let msg = "GridBlock's start_row_index should be greater than the last_block's start_row_index and its len".to_string();
                             return Err(CollaborateError::internal().context(msg))
                         }
-                        grid.blocks.push(block);
+                        grid.block_metas.push(block);
                     }
                 }
                 Ok(Some(()))
@@ -162,7 +162,7 @@ impl GridMetaPad {
     }
 
     pub fn get_blocks(&self) -> Vec<GridBlockMeta> {
-        self.grid_meta.blocks.clone()
+        self.grid_meta.block_metas.clone()
     }
 
     pub fn update_block(&mut self, changeset: GridBlockMetaChangeset) -> CollaborateResult<Option<GridChangeset>> {
@@ -226,12 +226,12 @@ impl GridMetaPad {
         F: FnOnce(&mut GridBlockMeta) -> CollaborateResult<Option<()>>,
     {
         self.modify_grid(
-            |grid| match grid.blocks.iter().position(|block| block.block_id == block_id) {
+            |grid| match grid.block_metas.iter().position(|block| block.block_id == block_id) {
                 None => {
                     tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id);
                     Ok(None)
                 }
-                Some(index) => f(&mut grid.blocks[index]),
+                Some(index) => f(&mut grid.block_metas[index]),
             },
         )
     }
@@ -279,7 +279,7 @@ impl std::default::Default for GridMetaPad {
         let grid = GridMeta {
             grid_id: uuid(),
             fields: vec![],
-            blocks: vec![],
+            block_metas: vec![],
         };
         let delta = make_grid_delta(&grid);
         GridMetaPad {

+ 2 - 2
shared-lib/flowy-collaboration/src/client_grid/mod.rs

@@ -1,7 +1,7 @@
-mod grid_block_meta_data_pad;
+mod grid_block_meta_pad;
 mod grid_builder;
 mod grid_meta_pad;
 
-pub use grid_block_meta_data_pad::*;
+pub use grid_block_meta_pad::*;
 pub use grid_builder::*;
 pub use grid_meta_pad::*;

+ 7 - 1
shared-lib/flowy-error-code/src/code.rs

@@ -85,8 +85,14 @@ pub enum ErrorCode {
     UserNotExist = 312,
     #[display(fmt = "Text is too long")]
     TextTooLong = 400,
+    #[display(fmt = "Grid block id is empty")]
+    BlockIdIsEmpty = 401,
+    #[display(fmt = "Row id is empty")]
+    RowIdIsEmpty = 402,
+    #[display(fmt = "Grid id is empty")]
+    GridIdIsEmpty = 403,
     #[display(fmt = "Invalid data")]
-    InvalidData = 401,
+    InvalidData = 404,
 }
 
 impl ErrorCode {

+ 15 - 5
shared-lib/flowy-error-code/src/protobuf/model/code.rs

@@ -56,7 +56,10 @@ pub enum ErrorCode {
     UserIdInvalid = 311,
     UserNotExist = 312,
     TextTooLong = 400,
-    InvalidData = 401,
+    BlockIdIsEmpty = 401,
+    RowIdIsEmpty = 402,
+    GridIdIsEmpty = 403,
+    InvalidData = 404,
 }
 
 impl ::protobuf::ProtobufEnum for ErrorCode {
@@ -97,7 +100,10 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             311 => ::std::option::Option::Some(ErrorCode::UserIdInvalid),
             312 => ::std::option::Option::Some(ErrorCode::UserNotExist),
             400 => ::std::option::Option::Some(ErrorCode::TextTooLong),
-            401 => ::std::option::Option::Some(ErrorCode::InvalidData),
+            401 => ::std::option::Option::Some(ErrorCode::BlockIdIsEmpty),
+            402 => ::std::option::Option::Some(ErrorCode::RowIdIsEmpty),
+            403 => ::std::option::Option::Some(ErrorCode::GridIdIsEmpty),
+            404 => ::std::option::Option::Some(ErrorCode::InvalidData),
             _ => ::std::option::Option::None
         }
     }
@@ -135,6 +141,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             ErrorCode::UserIdInvalid,
             ErrorCode::UserNotExist,
             ErrorCode::TextTooLong,
+            ErrorCode::BlockIdIsEmpty,
+            ErrorCode::RowIdIsEmpty,
+            ErrorCode::GridIdIsEmpty,
             ErrorCode::InvalidData,
         ];
         values
@@ -164,7 +173,7 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\ncode.proto*\xe8\x05\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
+    \n\ncode.proto*\xa4\x06\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
     \n\x10UserUnauthorized\x10\x02\x12\x12\n\x0eRecordNotFound\x10\x03\x12\
     \x18\n\x14WorkspaceNameInvalid\x10d\x12\x16\n\x12WorkspaceIdInvalid\x10e\
     \x12\x18\n\x14AppColorStyleInvalid\x10f\x12\x18\n\x14WorkspaceDescTooLon\
@@ -181,8 +190,9 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x14\n\x0fUserNameTooLong\x10\xb4\x02\x12'\n\"UserNameContainForbiddenCh\
     aracters\x10\xb5\x02\x12\x14\n\x0fUserNameIsEmpty\x10\xb6\x02\x12\x12\n\
     \rUserIdInvalid\x10\xb7\x02\x12\x11\n\x0cUserNotExist\x10\xb8\x02\x12\
-    \x10\n\x0bTextTooLong\x10\x90\x03\x12\x10\n\x0bInvalidData\x10\x91\x03b\
-    \x06proto3\
+    \x10\n\x0bTextTooLong\x10\x90\x03\x12\x13\n\x0eBlockIdIsEmpty\x10\x91\
+    \x03\x12\x11\n\x0cRowIdIsEmpty\x10\x92\x03\x12\x12\n\rGridIdIsEmpty\x10\
+    \x93\x03\x12\x10\n\x0bInvalidData\x10\x94\x03b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 4 - 1
shared-lib/flowy-error-code/src/protobuf/proto/code.proto

@@ -32,5 +32,8 @@ enum ErrorCode {
     UserIdInvalid = 311;
     UserNotExist = 312;
     TextTooLong = 400;
-    InvalidData = 401;
+    BlockIdIsEmpty = 401;
+    RowIdIsEmpty = 402;
+    GridIdIsEmpty = 403;
+    InvalidData = 404;
 }

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

@@ -14,6 +14,7 @@ strum_macros = "0.21"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
 uuid = { version = "0.8", features = ["serde", "v4"] }
+flowy-error-code = { path = "../flowy-error-code"}
 
 [build-dependencies]
 lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }

+ 40 - 8
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -1,7 +1,9 @@
-use crate::entities::{FieldMeta, FieldType, GridBlockMeta, RowMeta};
+use crate::entities::{FieldMeta, FieldType, RowMeta};
 use flowy_derive::ProtoBuf;
 use std::collections::HashMap;
-use std::hash::Hash;
+
+use crate::parser::NonEmptyId;
+use flowy_error_code::ErrorCode;
 use std::sync::Arc;
 
 #[derive(Debug, Clone, Default, ProtoBuf)]
@@ -13,7 +15,7 @@ pub struct Grid {
     pub field_orders: Vec<FieldOrder>,
 
     #[pb(index = 3)]
-    pub blocks: Vec<GridBlockMeta>,
+    pub block_orders: Vec<GridBlockOrder>,
 }
 
 #[derive(Debug, Clone, Default, ProtoBuf)]
@@ -169,6 +171,18 @@ pub struct Row {
     pub height: i32,
 }
 
+#[derive(Debug, Default, ProtoBuf)]
+pub struct RepeatedRow {
+    #[pb(index = 1)]
+    pub items: Vec<Row>,
+}
+
+impl std::convert::From<Vec<Row>> for RepeatedRow {
+    fn from(items: Vec<Row>) -> Self {
+        Self { items }
+    }
+}
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct RepeatedGridBlock {
     #[pb(index = 1)]
@@ -181,20 +195,26 @@ impl std::convert::From<Vec<GridBlock>> for RepeatedGridBlock {
     }
 }
 
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct GridBlockOrder {
+    #[pb(index = 1)]
+    pub block_id: String,
+}
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct GridBlock {
     #[pb(index = 1)]
     pub block_id: String,
 
     #[pb(index = 2)]
-    pub rows: Vec<String>,
+    pub row_ids: Vec<String>,
 }
 
 impl GridBlock {
-    pub fn new(block_id: &str, rows: Vec<Row>) -> Self {
+    pub fn new(block_id: &str, row_ids: Vec<String>) -> Self {
         Self {
             block_id: block_id.to_owned(),
-            rows,
+            row_ids,
         }
     }
 }
@@ -278,7 +298,7 @@ pub struct CreateRowPayload {
     pub grid_id: String,
 
     #[pb(index = 2, one_of)]
-    pub upper_row_id: Option<String>,
+    pub start_row_id: Option<String>,
 }
 
 #[derive(ProtoBuf, Default)]
@@ -296,5 +316,17 @@ pub struct QueryGridBlocksPayload {
     pub grid_id: String,
 
     #[pb(index = 2)]
-    pub blocks: Vec<GridBlockMeta>,
+    pub block_orders: Vec<GridBlockOrder>,
+}
+
+#[derive(ProtoBuf, Default)]
+pub struct QueryRowPayload {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub block_id: String,
+
+    #[pb(index = 3)]
+    pub row_id: String,
 }

+ 9 - 8
shared-lib/flowy-grid-data-model/src/entities/meta.rs

@@ -2,6 +2,7 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 
+use crate::entities::GridBlockOrder;
 use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
 
 pub const DEFAULT_ROW_HEIGHT: i32 = 36;
@@ -16,7 +17,7 @@ pub struct GridMeta {
     pub fields: Vec<FieldMeta>,
 
     #[pb(index = 3)]
-    pub blocks: Vec<GridBlockMeta>,
+    pub block_metas: Vec<GridBlockMeta>,
 }
 
 #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
@@ -33,7 +34,7 @@ pub struct GridBlockMeta {
 
 impl GridBlockMeta {
     pub fn len(&self) -> i32 {
-        self.start_row_index + self.row_count
+        self.row_count
     }
 
     pub fn is_empty(&self) -> bool {
@@ -67,7 +68,7 @@ impl GridBlockMetaChangeset {
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
-pub struct GridBlockMetaData {
+pub struct GridBlockMetaSerde {
     #[pb(index = 1)]
     pub block_id: String,
 
@@ -329,24 +330,24 @@ pub struct BuildGridContext {
     pub field_metas: Vec<FieldMeta>,
 
     #[pb(index = 2)]
-    pub grid_block: GridBlockMeta,
+    pub block_metas: GridBlockMeta,
 
     #[pb(index = 3)]
-    pub grid_block_meta_data: GridBlockMetaData,
+    pub block_meta_data: GridBlockMetaSerde,
 }
 
 impl std::default::Default for BuildGridContext {
     fn default() -> Self {
         let grid_block = GridBlockMeta::new();
-        let grid_block_meta_data = GridBlockMetaData {
+        let grid_block_meta_data = GridBlockMetaSerde {
             block_id: grid_block.block_id.clone(),
             row_metas: vec![],
         };
 
         Self {
             field_metas: vec![],
-            grid_block,
-            grid_block_meta_data,
+            block_metas: grid_block,
+            block_meta_data: grid_block_meta_data,
         }
     }
 }

+ 1 - 0
shared-lib/flowy-grid-data-model/src/lib.rs

@@ -1,2 +1,3 @@
 pub mod entities;
+pub mod parser;
 pub mod protobuf;

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

@@ -0,0 +1,82 @@
+use crate::entities::{
+    CreateRowPayload, GridBlockOrder, QueryFieldPayload, QueryGridBlocksPayload, QueryRowPayload, RepeatedFieldOrder,
+};
+use crate::parser::NonEmptyId;
+use flowy_error_code::ErrorCode;
+
+#[derive(Default)]
+pub struct CreateRowParams {
+    pub grid_id: String,
+    pub start_row_id: Option<String>,
+}
+
+impl TryInto<CreateRowParams> for CreateRowPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateRowParams, Self::Error> {
+        let grid_id = NonEmptyId::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        Ok(CreateRowParams {
+            grid_id: grid_id.0,
+            start_row_id: self.start_row_id,
+        })
+    }
+}
+
+#[derive(Default)]
+pub struct QueryFieldParams {
+    pub grid_id: String,
+    pub field_orders: RepeatedFieldOrder,
+}
+
+impl TryInto<QueryFieldParams> for QueryFieldPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<QueryFieldParams, Self::Error> {
+        let grid_id = NonEmptyId::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        Ok(QueryFieldParams {
+            grid_id: grid_id.0,
+            field_orders: self.field_orders,
+        })
+    }
+}
+
+#[derive(Default)]
+pub struct QueryGridBlocksParams {
+    pub grid_id: String,
+    pub block_orders: Vec<GridBlockOrder>,
+}
+
+impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<QueryGridBlocksParams, Self::Error> {
+        let grid_id = NonEmptyId::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        Ok(QueryGridBlocksParams {
+            grid_id: grid_id.0,
+            block_orders: self.block_orders,
+        })
+    }
+}
+
+#[derive(Default)]
+pub struct QueryRowParams {
+    pub grid_id: String,
+    pub block_id: String,
+    pub row_id: String,
+}
+
+impl TryInto<QueryRowParams> for QueryRowPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<QueryRowParams, Self::Error> {
+        let grid_id = NonEmptyId::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let block_id = NonEmptyId::parse(self.block_id).map_err(|_| ErrorCode::BlockIdIsEmpty)?;
+        let row_id = NonEmptyId::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
+
+        Ok(QueryRowParams {
+            grid_id: grid_id.0,
+            block_id: block_id.0,
+            row_id: row_id.0,
+        })
+    }
+}

+ 20 - 0
shared-lib/flowy-grid-data-model/src/parser/id.rs

@@ -0,0 +1,20 @@
+use flowy_error_code::ErrorCode;
+
+#[derive(Debug)]
+pub struct NonEmptyId(pub String);
+
+impl NonEmptyId {
+    pub fn parse(s: String) -> Result<NonEmptyId, ()> {
+        if s.trim().is_empty() {
+            return Err(());
+        }
+
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for NonEmptyId {
+    fn as_ref(&self) -> &str {
+        &self.0
+    }
+}

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

@@ -0,0 +1,5 @@
+mod grid;
+mod id;
+
+pub use grid::*;
+pub use id::*;

+ 704 - 139
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -28,7 +28,7 @@ pub struct Grid {
     // message fields
     pub id: ::std::string::String,
     pub field_orders: ::protobuf::RepeatedField<FieldOrder>,
-    pub blocks: ::protobuf::RepeatedField<super::meta::GridBlockMeta>,
+    pub block_orders: ::protobuf::RepeatedField<GridBlockOrder>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -96,29 +96,29 @@ impl Grid {
         ::std::mem::replace(&mut self.field_orders, ::protobuf::RepeatedField::new())
     }
 
-    // repeated .GridBlockMeta blocks = 3;
+    // repeated .GridBlockOrder block_orders = 3;
 
 
-    pub fn get_blocks(&self) -> &[super::meta::GridBlockMeta] {
-        &self.blocks
+    pub fn get_block_orders(&self) -> &[GridBlockOrder] {
+        &self.block_orders
     }
-    pub fn clear_blocks(&mut self) {
-        self.blocks.clear();
+    pub fn clear_block_orders(&mut self) {
+        self.block_orders.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField<super::meta::GridBlockMeta>) {
-        self.blocks = v;
+    pub fn set_block_orders(&mut self, v: ::protobuf::RepeatedField<GridBlockOrder>) {
+        self.block_orders = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField<super::meta::GridBlockMeta> {
-        &mut self.blocks
+    pub fn mut_block_orders(&mut self) -> &mut ::protobuf::RepeatedField<GridBlockOrder> {
+        &mut self.block_orders
     }
 
     // Take field
-    pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField<super::meta::GridBlockMeta> {
-        ::std::mem::replace(&mut self.blocks, ::protobuf::RepeatedField::new())
+    pub fn take_block_orders(&mut self) -> ::protobuf::RepeatedField<GridBlockOrder> {
+        ::std::mem::replace(&mut self.block_orders, ::protobuf::RepeatedField::new())
     }
 }
 
@@ -129,7 +129,7 @@ impl ::protobuf::Message for Grid {
                 return false;
             }
         };
-        for v in &self.blocks {
+        for v in &self.block_orders {
             if !v.is_initialized() {
                 return false;
             }
@@ -148,7 +148,7 @@ impl ::protobuf::Message for Grid {
                     ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.field_orders)?;
                 },
                 3 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.blocks)?;
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.block_orders)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -169,7 +169,7 @@ impl ::protobuf::Message for Grid {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
-        for value in &self.blocks {
+        for value in &self.block_orders {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
@@ -187,7 +187,7 @@ impl ::protobuf::Message for Grid {
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
         };
-        for v in &self.blocks {
+        for v in &self.block_orders {
             os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
@@ -240,10 +240,10 @@ impl ::protobuf::Message for Grid {
                 |m: &Grid| { &m.field_orders },
                 |m: &mut Grid| { &mut m.field_orders },
             ));
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::meta::GridBlockMeta>>(
-                "blocks",
-                |m: &Grid| { &m.blocks },
-                |m: &mut Grid| { &mut m.blocks },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockOrder>>(
+                "block_orders",
+                |m: &Grid| { &m.block_orders },
+                |m: &mut Grid| { &mut m.block_orders },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<Grid>(
                 "Grid",
@@ -263,7 +263,7 @@ impl ::protobuf::Clear for Grid {
     fn clear(&mut self) {
         self.id.clear();
         self.field_orders.clear();
-        self.blocks.clear();
+        self.block_orders.clear();
         self.unknown_fields.clear();
     }
 }
@@ -1748,6 +1748,172 @@ impl ::protobuf::reflect::ProtobufValue for Row {
     }
 }
 
+#[derive(PartialEq,Clone,Default)]
+pub struct RepeatedRow {
+    // message fields
+    pub items: ::protobuf::RepeatedField<Row>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a RepeatedRow {
+    fn default() -> &'a RepeatedRow {
+        <RepeatedRow as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl RepeatedRow {
+    pub fn new() -> RepeatedRow {
+        ::std::default::Default::default()
+    }
+
+    // repeated .Row items = 1;
+
+
+    pub fn get_items(&self) -> &[Row] {
+        &self.items
+    }
+    pub fn clear_items(&mut self) {
+        self.items.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_items(&mut self, v: ::protobuf::RepeatedField<Row>) {
+        self.items = v;
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_items(&mut self) -> &mut ::protobuf::RepeatedField<Row> {
+        &mut self.items
+    }
+
+    // Take field
+    pub fn take_items(&mut self) -> ::protobuf::RepeatedField<Row> {
+        ::std::mem::replace(&mut self.items, ::protobuf::RepeatedField::new())
+    }
+}
+
+impl ::protobuf::Message for RepeatedRow {
+    fn is_initialized(&self) -> bool {
+        for v in &self.items {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.items)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        for value in &self.items {
+            let len = value.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        };
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        for v in &self.items {
+            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        };
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> RepeatedRow {
+        RepeatedRow::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Row>>(
+                "items",
+                |m: &RepeatedRow| { &m.items },
+                |m: &mut RepeatedRow| { &mut m.items },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<RepeatedRow>(
+                "RepeatedRow",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static RepeatedRow {
+        static instance: ::protobuf::rt::LazyV2<RepeatedRow> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(RepeatedRow::new)
+    }
+}
+
+impl ::protobuf::Clear for RepeatedRow {
+    fn clear(&mut self) {
+        self.items.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for RepeatedRow {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for RepeatedRow {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
 #[derive(PartialEq,Clone,Default)]
 pub struct RepeatedGridBlock {
     // message fields
@@ -1914,11 +2080,170 @@ impl ::protobuf::reflect::ProtobufValue for RepeatedGridBlock {
     }
 }
 
+#[derive(PartialEq,Clone,Default)]
+pub struct GridBlockOrder {
+    // message fields
+    pub block_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a GridBlockOrder {
+    fn default() -> &'a GridBlockOrder {
+        <GridBlockOrder as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl GridBlockOrder {
+    pub fn new() -> GridBlockOrder {
+        ::std::default::Default::default()
+    }
+
+    // string block_id = 1;
+
+
+    pub fn get_block_id(&self) -> &str {
+        &self.block_id
+    }
+    pub fn clear_block_id(&mut self) {
+        self.block_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_block_id(&mut self, v: ::std::string::String) {
+        self.block_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_block_id(&mut self) -> &mut ::std::string::String {
+        &mut self.block_id
+    }
+
+    // Take field
+    pub fn take_block_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.block_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for GridBlockOrder {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.block_id)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.block_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.block_id);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.block_id.is_empty() {
+            os.write_string(1, &self.block_id)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> GridBlockOrder {
+        GridBlockOrder::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "block_id",
+                |m: &GridBlockOrder| { &m.block_id },
+                |m: &mut GridBlockOrder| { &mut m.block_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlockOrder>(
+                "GridBlockOrder",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static GridBlockOrder {
+        static instance: ::protobuf::rt::LazyV2<GridBlockOrder> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(GridBlockOrder::new)
+    }
+}
+
+impl ::protobuf::Clear for GridBlockOrder {
+    fn clear(&mut self) {
+        self.block_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for GridBlockOrder {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for GridBlockOrder {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
 #[derive(PartialEq,Clone,Default)]
 pub struct GridBlock {
     // message fields
     pub block_id: ::std::string::String,
-    pub rows: ::protobuf::RepeatedField<Row>,
+    pub row_ids: ::protobuf::RepeatedField<::std::string::String>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -1961,39 +2286,34 @@ impl GridBlock {
         ::std::mem::replace(&mut self.block_id, ::std::string::String::new())
     }
 
-    // repeated .Row rows = 2;
+    // repeated string row_ids = 2;
 
 
-    pub fn get_rows(&self) -> &[Row] {
-        &self.rows
+    pub fn get_row_ids(&self) -> &[::std::string::String] {
+        &self.row_ids
     }
-    pub fn clear_rows(&mut self) {
-        self.rows.clear();
+    pub fn clear_row_ids(&mut self) {
+        self.row_ids.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_rows(&mut self, v: ::protobuf::RepeatedField<Row>) {
-        self.rows = v;
+    pub fn set_row_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
+        self.row_ids = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_rows(&mut self) -> &mut ::protobuf::RepeatedField<Row> {
-        &mut self.rows
+    pub fn mut_row_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
+        &mut self.row_ids
     }
 
     // Take field
-    pub fn take_rows(&mut self) -> ::protobuf::RepeatedField<Row> {
-        ::std::mem::replace(&mut self.rows, ::protobuf::RepeatedField::new())
+    pub fn take_row_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
+        ::std::mem::replace(&mut self.row_ids, ::protobuf::RepeatedField::new())
     }
 }
 
 impl ::protobuf::Message for GridBlock {
     fn is_initialized(&self) -> bool {
-        for v in &self.rows {
-            if !v.is_initialized() {
-                return false;
-            }
-        };
         true
     }
 
@@ -2005,7 +2325,7 @@ impl ::protobuf::Message for GridBlock {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.block_id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.rows)?;
+                    ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.row_ids)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -2022,9 +2342,8 @@ impl ::protobuf::Message for GridBlock {
         if !self.block_id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.block_id);
         }
-        for value in &self.rows {
-            let len = value.compute_size();
-            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        for value in &self.row_ids {
+            my_size += ::protobuf::rt::string_size(2, &value);
         };
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -2035,10 +2354,8 @@ impl ::protobuf::Message for GridBlock {
         if !self.block_id.is_empty() {
             os.write_string(1, &self.block_id)?;
         }
-        for v in &self.rows {
-            os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
-            os.write_raw_varint32(v.get_cached_size())?;
-            v.write_to_with_cached_sizes(os)?;
+        for v in &self.row_ids {
+            os.write_string(2, &v)?;
         };
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -2083,10 +2400,10 @@ impl ::protobuf::Message for GridBlock {
                 |m: &GridBlock| { &m.block_id },
                 |m: &mut GridBlock| { &mut m.block_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Row>>(
-                "rows",
-                |m: &GridBlock| { &m.rows },
-                |m: &mut GridBlock| { &mut m.rows },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "row_ids",
+                |m: &GridBlock| { &m.row_ids },
+                |m: &mut GridBlock| { &mut m.row_ids },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlock>(
                 "GridBlock",
@@ -2105,7 +2422,7 @@ impl ::protobuf::Message for GridBlock {
 impl ::protobuf::Clear for GridBlock {
     fn clear(&mut self) {
         self.block_id.clear();
-        self.rows.clear();
+        self.row_ids.clear();
         self.unknown_fields.clear();
     }
 }
@@ -2971,7 +3288,7 @@ pub struct CreateRowPayload {
     // message fields
     pub grid_id: ::std::string::String,
     // message oneof groups
-    pub one_of_upper_row_id: ::std::option::Option<CreateRowPayload_oneof_one_of_upper_row_id>,
+    pub one_of_start_row_id: ::std::option::Option<CreateRowPayload_oneof_one_of_start_row_id>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -2984,8 +3301,8 @@ impl<'a> ::std::default::Default for &'a CreateRowPayload {
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum CreateRowPayload_oneof_one_of_upper_row_id {
-    upper_row_id(::std::string::String),
+pub enum CreateRowPayload_oneof_one_of_start_row_id {
+    start_row_id(::std::string::String),
 }
 
 impl CreateRowPayload {
@@ -3019,48 +3336,48 @@ impl CreateRowPayload {
         ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
     }
 
-    // string upper_row_id = 2;
+    // string start_row_id = 2;
 
 
-    pub fn get_upper_row_id(&self) -> &str {
-        match self.one_of_upper_row_id {
-            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(ref v)) => v,
+    pub fn get_start_row_id(&self) -> &str {
+        match self.one_of_start_row_id {
+            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(ref v)) => v,
             _ => "",
         }
     }
-    pub fn clear_upper_row_id(&mut self) {
-        self.one_of_upper_row_id = ::std::option::Option::None;
+    pub fn clear_start_row_id(&mut self) {
+        self.one_of_start_row_id = ::std::option::Option::None;
     }
 
-    pub fn has_upper_row_id(&self) -> bool {
-        match self.one_of_upper_row_id {
-            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(..)) => true,
+    pub fn has_start_row_id(&self) -> bool {
+        match self.one_of_start_row_id {
+            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
-    pub fn set_upper_row_id(&mut self, v: ::std::string::String) {
-        self.one_of_upper_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(v))
+    pub fn set_start_row_id(&mut self, v: ::std::string::String) {
+        self.one_of_start_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(v))
     }
 
     // Mutable pointer to the field.
-    pub fn mut_upper_row_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(_)) = self.one_of_upper_row_id {
+    pub fn mut_start_row_id(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(_)) = self.one_of_start_row_id {
         } else {
-            self.one_of_upper_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(::std::string::String::new()));
+            self.one_of_start_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(::std::string::String::new()));
         }
-        match self.one_of_upper_row_id {
-            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(ref mut v)) => v,
+        match self.one_of_start_row_id {
+            ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(ref mut v)) => v,
             _ => panic!(),
         }
     }
 
     // Take field
-    pub fn take_upper_row_id(&mut self) -> ::std::string::String {
-        if self.has_upper_row_id() {
-            match self.one_of_upper_row_id.take() {
-                ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(v)) => v,
+    pub fn take_start_row_id(&mut self) -> ::std::string::String {
+        if self.has_start_row_id() {
+            match self.one_of_start_row_id.take() {
+                ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -3085,7 +3402,7 @@ impl ::protobuf::Message for CreateRowPayload {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_upper_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(is.read_string()?));
+                    self.one_of_start_row_id = ::std::option::Option::Some(CreateRowPayload_oneof_one_of_start_row_id::start_row_id(is.read_string()?));
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -3102,9 +3419,9 @@ impl ::protobuf::Message for CreateRowPayload {
         if !self.grid_id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.grid_id);
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_upper_row_id {
+        if let ::std::option::Option::Some(ref v) = self.one_of_start_row_id {
             match v {
-                &CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(ref v) => {
+                &CreateRowPayload_oneof_one_of_start_row_id::start_row_id(ref v) => {
                     my_size += ::protobuf::rt::string_size(2, &v);
                 },
             };
@@ -3118,9 +3435,9 @@ impl ::protobuf::Message for CreateRowPayload {
         if !self.grid_id.is_empty() {
             os.write_string(1, &self.grid_id)?;
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_upper_row_id {
+        if let ::std::option::Option::Some(ref v) = self.one_of_start_row_id {
             match v {
-                &CreateRowPayload_oneof_one_of_upper_row_id::upper_row_id(ref v) => {
+                &CreateRowPayload_oneof_one_of_start_row_id::start_row_id(ref v) => {
                     os.write_string(2, v)?;
                 },
             };
@@ -3169,9 +3486,9 @@ impl ::protobuf::Message for CreateRowPayload {
                 |m: &mut CreateRowPayload| { &mut m.grid_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
-                "upper_row_id",
-                CreateRowPayload::has_upper_row_id,
-                CreateRowPayload::get_upper_row_id,
+                "start_row_id",
+                CreateRowPayload::has_start_row_id,
+                CreateRowPayload::get_start_row_id,
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateRowPayload>(
                 "CreateRowPayload",
@@ -3190,7 +3507,7 @@ impl ::protobuf::Message for CreateRowPayload {
 impl ::protobuf::Clear for CreateRowPayload {
     fn clear(&mut self) {
         self.grid_id.clear();
-        self.one_of_upper_row_id = ::std::option::Option::None;
+        self.one_of_start_row_id = ::std::option::Option::None;
         self.unknown_fields.clear();
     }
 }
@@ -3427,7 +3744,7 @@ impl ::protobuf::reflect::ProtobufValue for QueryFieldPayload {
 pub struct QueryGridBlocksPayload {
     // message fields
     pub grid_id: ::std::string::String,
-    pub blocks: ::protobuf::RepeatedField<super::meta::GridBlockMeta>,
+    pub block_orders: ::protobuf::RepeatedField<GridBlockOrder>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -3470,35 +3787,35 @@ impl QueryGridBlocksPayload {
         ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
     }
 
-    // repeated .GridBlockMeta blocks = 2;
+    // repeated .GridBlockOrder block_orders = 2;
 
 
-    pub fn get_blocks(&self) -> &[super::meta::GridBlockMeta] {
-        &self.blocks
+    pub fn get_block_orders(&self) -> &[GridBlockOrder] {
+        &self.block_orders
     }
-    pub fn clear_blocks(&mut self) {
-        self.blocks.clear();
+    pub fn clear_block_orders(&mut self) {
+        self.block_orders.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField<super::meta::GridBlockMeta>) {
-        self.blocks = v;
+    pub fn set_block_orders(&mut self, v: ::protobuf::RepeatedField<GridBlockOrder>) {
+        self.block_orders = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField<super::meta::GridBlockMeta> {
-        &mut self.blocks
+    pub fn mut_block_orders(&mut self) -> &mut ::protobuf::RepeatedField<GridBlockOrder> {
+        &mut self.block_orders
     }
 
     // Take field
-    pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField<super::meta::GridBlockMeta> {
-        ::std::mem::replace(&mut self.blocks, ::protobuf::RepeatedField::new())
+    pub fn take_block_orders(&mut self) -> ::protobuf::RepeatedField<GridBlockOrder> {
+        ::std::mem::replace(&mut self.block_orders, ::protobuf::RepeatedField::new())
     }
 }
 
 impl ::protobuf::Message for QueryGridBlocksPayload {
     fn is_initialized(&self) -> bool {
-        for v in &self.blocks {
+        for v in &self.block_orders {
             if !v.is_initialized() {
                 return false;
             }
@@ -3514,7 +3831,7 @@ impl ::protobuf::Message for QueryGridBlocksPayload {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.blocks)?;
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.block_orders)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -3531,7 +3848,7 @@ impl ::protobuf::Message for QueryGridBlocksPayload {
         if !self.grid_id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.grid_id);
         }
-        for value in &self.blocks {
+        for value in &self.block_orders {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
@@ -3544,7 +3861,7 @@ impl ::protobuf::Message for QueryGridBlocksPayload {
         if !self.grid_id.is_empty() {
             os.write_string(1, &self.grid_id)?;
         }
-        for v in &self.blocks {
+        for v in &self.block_orders {
             os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
@@ -3592,10 +3909,10 @@ impl ::protobuf::Message for QueryGridBlocksPayload {
                 |m: &QueryGridBlocksPayload| { &m.grid_id },
                 |m: &mut QueryGridBlocksPayload| { &mut m.grid_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::meta::GridBlockMeta>>(
-                "blocks",
-                |m: &QueryGridBlocksPayload| { &m.blocks },
-                |m: &mut QueryGridBlocksPayload| { &mut m.blocks },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockOrder>>(
+                "block_orders",
+                |m: &QueryGridBlocksPayload| { &m.block_orders },
+                |m: &mut QueryGridBlocksPayload| { &mut m.block_orders },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryGridBlocksPayload>(
                 "QueryGridBlocksPayload",
@@ -3614,7 +3931,7 @@ impl ::protobuf::Message for QueryGridBlocksPayload {
 impl ::protobuf::Clear for QueryGridBlocksPayload {
     fn clear(&mut self) {
         self.grid_id.clear();
-        self.blocks.clear();
+        self.block_orders.clear();
         self.unknown_fields.clear();
     }
 }
@@ -3631,43 +3948,291 @@ impl ::protobuf::reflect::ProtobufValue for QueryGridBlocksPayload {
     }
 }
 
+#[derive(PartialEq,Clone,Default)]
+pub struct QueryRowPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub block_id: ::std::string::String,
+    pub row_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a QueryRowPayload {
+    fn default() -> &'a QueryRowPayload {
+        <QueryRowPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl QueryRowPayload {
+    pub fn new() -> QueryRowPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string block_id = 2;
+
+
+    pub fn get_block_id(&self) -> &str {
+        &self.block_id
+    }
+    pub fn clear_block_id(&mut self) {
+        self.block_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_block_id(&mut self, v: ::std::string::String) {
+        self.block_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_block_id(&mut self) -> &mut ::std::string::String {
+        &mut self.block_id
+    }
+
+    // Take field
+    pub fn take_block_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.block_id, ::std::string::String::new())
+    }
+
+    // string row_id = 3;
+
+
+    pub fn get_row_id(&self) -> &str {
+        &self.row_id
+    }
+    pub fn clear_row_id(&mut self) {
+        self.row_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_row_id(&mut self, v: ::std::string::String) {
+        self.row_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
+        &mut self.row_id
+    }
+
+    // Take field
+    pub fn take_row_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.row_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for QueryRowPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.block_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.block_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.block_id);
+        }
+        if !self.row_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.row_id);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.block_id.is_empty() {
+            os.write_string(2, &self.block_id)?;
+        }
+        if !self.row_id.is_empty() {
+            os.write_string(3, &self.row_id)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> QueryRowPayload {
+        QueryRowPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &QueryRowPayload| { &m.grid_id },
+                |m: &mut QueryRowPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "block_id",
+                |m: &QueryRowPayload| { &m.block_id },
+                |m: &mut QueryRowPayload| { &mut m.block_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "row_id",
+                |m: &QueryRowPayload| { &m.row_id },
+                |m: &mut QueryRowPayload| { &mut m.row_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryRowPayload>(
+                "QueryRowPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static QueryRowPayload {
+        static instance: ::protobuf::rt::LazyV2<QueryRowPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(QueryRowPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for QueryRowPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.block_id.clear();
+        self.row_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for QueryRowPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for QueryRowPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\ngrid.proto\x1a\nmeta.proto\"n\n\x04Grid\x12\x0e\n\x02id\x18\x01\x20\
+    \n\ngrid.proto\x1a\nmeta.proto\"z\n\x04Grid\x12\x0e\n\x02id\x18\x01\x20\
     \x01(\tR\x02id\x12.\n\x0cfield_orders\x18\x02\x20\x03(\x0b2\x0b.FieldOrd\
-    erR\x0bfieldOrders\x12&\n\x06blocks\x18\x03\x20\x03(\x0b2\x0e.GridBlockM\
-    etaR\x06blocks\"\xb8\x01\n\x05Field\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
-    \x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\
-    \x18\x03\x20\x01(\tR\x04desc\x12)\n\nfield_type\x18\x04\x20\x01(\x0e2\n.\
-    FieldTypeR\tfieldType\x12\x16\n\x06frozen\x18\x05\x20\x01(\x08R\x06froze\
-    n\x12\x1e\n\nvisibility\x18\x06\x20\x01(\x08R\nvisibility\x12\x14\n\x05w\
-    idth\x18\x07\x20\x01(\x05R\x05width\"'\n\nFieldOrder\x12\x19\n\x08field_\
-    id\x18\x01\x20\x01(\tR\x07fieldId\"-\n\rRepeatedField\x12\x1c\n\x05items\
-    \x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\
-    \x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05items\"<\n\x08\
-    RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08b\
-    lock_id\x18\x02\x20\x01(\tR\x07blockId\"3\n\x10RepeatedRowOrder\x12\x1f\
-    \n\x05items\x18\x01\x20\x03(\x0b2\t.RowOrderR\x05items\"\xb8\x01\n\x03Ro\
-    w\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_field_id\
-    \x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFieldId\x12\
-    \x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellByFieldId\
-    Entry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\
-    \x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\"5\n\x11RepeatedGridBloc\
-    k\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"@\n\tG\
-    ridBlock\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12\x18\n\
-    \x04rows\x18\x02\x20\x03(\x0b2\x04.RowR\x04rows\";\n\x04Cell\x12\x19\n\
-    \x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\
-    \x20\x01(\tR\x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\
-    \x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\
-    \x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05valu\
-    e\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\
-    \x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid\
-    _id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cupper_row_id\x18\x02\x20\
-    \x01(\tH\0R\nupperRowIdB\x15\n\x13one_of_upper_row_id\"d\n\x11QueryField\
-    Payload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfie\
-    ld_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"\
-    Y\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
-    \x06gridId\x12&\n\x06blocks\x18\x02\x20\x03(\x0b2\x0e.GridBlockMetaR\x06\
-    blocksb\x06proto3\
+    erR\x0bfieldOrders\x122\n\x0cblock_orders\x18\x03\x20\x03(\x0b2\x0f.Grid\
+    BlockOrderR\x0bblockOrders\"\xb8\x01\n\x05Field\x12\x0e\n\x02id\x18\x01\
+    \x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\
+    \n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12)\n\nfield_type\x18\x04\x20\
+    \x01(\x0e2\n.FieldTypeR\tfieldType\x12\x16\n\x06frozen\x18\x05\x20\x01(\
+    \x08R\x06frozen\x12\x1e\n\nvisibility\x18\x06\x20\x01(\x08R\nvisibility\
+    \x12\x14\n\x05width\x18\x07\x20\x01(\x05R\x05width\"'\n\nFieldOrder\x12\
+    \x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\"-\n\rRepeatedField\
+    \x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12Re\
+    peatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\
+    \x05items\"<\n\x08RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05ro\
+    wId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\"3\n\x10Repeate\
+    dRowOrder\x12\x1f\n\x05items\x18\x01\x20\x03(\x0b2\t.RowOrderR\x05items\
+    \"\xb8\x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10\
+    cell_by_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcell\
+    ByFieldId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12\
+    CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\
+    \x05value\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRep\
+    eatedRow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\
+    \x11RepeatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlo\
+    ckR\x05items\"+\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\
+    \x01(\tR\x07blockId\"?\n\tGridBlock\x12\x19\n\x08block_id\x18\x01\x20\
+    \x01(\tR\x07blockId\x12\x17\n\x07row_ids\x18\x02\x20\x03(\tR\x06rowIds\"\
+    ;\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\
+    \n\x07content\x18\x02\x20\x01(\tR\x07content\"+\n\x0cRepeatedCell\x12\
+    \x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateG\
+    ridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06Grid\
+    Id\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\
+    \x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayloa\
+    d\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_ro\
+    w_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"d\
+    \n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06grid\
+    Id\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\
+    \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\
+    \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\
+    \x0f.GridBlockOrderR\x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\
+    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\
+    \x20\x01(\tR\x07blockId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\
+    b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 112 - 113
shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs

@@ -28,7 +28,7 @@ pub struct GridMeta {
     // message fields
     pub grid_id: ::std::string::String,
     pub fields: ::protobuf::RepeatedField<FieldMeta>,
-    pub blocks: ::protobuf::RepeatedField<GridBlockMeta>,
+    pub block_metas: ::protobuf::RepeatedField<GridBlockMeta>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -96,29 +96,29 @@ impl GridMeta {
         ::std::mem::replace(&mut self.fields, ::protobuf::RepeatedField::new())
     }
 
-    // repeated .GridBlockMeta blocks = 3;
+    // repeated .GridBlockMeta block_metas = 3;
 
 
-    pub fn get_blocks(&self) -> &[GridBlockMeta] {
-        &self.blocks
+    pub fn get_block_metas(&self) -> &[GridBlockMeta] {
+        &self.block_metas
     }
-    pub fn clear_blocks(&mut self) {
-        self.blocks.clear();
+    pub fn clear_block_metas(&mut self) {
+        self.block_metas.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField<GridBlockMeta>) {
-        self.blocks = v;
+    pub fn set_block_metas(&mut self, v: ::protobuf::RepeatedField<GridBlockMeta>) {
+        self.block_metas = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField<GridBlockMeta> {
-        &mut self.blocks
+    pub fn mut_block_metas(&mut self) -> &mut ::protobuf::RepeatedField<GridBlockMeta> {
+        &mut self.block_metas
     }
 
     // Take field
-    pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField<GridBlockMeta> {
-        ::std::mem::replace(&mut self.blocks, ::protobuf::RepeatedField::new())
+    pub fn take_block_metas(&mut self) -> ::protobuf::RepeatedField<GridBlockMeta> {
+        ::std::mem::replace(&mut self.block_metas, ::protobuf::RepeatedField::new())
     }
 }
 
@@ -129,7 +129,7 @@ impl ::protobuf::Message for GridMeta {
                 return false;
             }
         };
-        for v in &self.blocks {
+        for v in &self.block_metas {
             if !v.is_initialized() {
                 return false;
             }
@@ -148,7 +148,7 @@ impl ::protobuf::Message for GridMeta {
                     ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.fields)?;
                 },
                 3 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.blocks)?;
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.block_metas)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -169,7 +169,7 @@ impl ::protobuf::Message for GridMeta {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
-        for value in &self.blocks {
+        for value in &self.block_metas {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
@@ -187,7 +187,7 @@ impl ::protobuf::Message for GridMeta {
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
         };
-        for v in &self.blocks {
+        for v in &self.block_metas {
             os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
@@ -241,9 +241,9 @@ impl ::protobuf::Message for GridMeta {
                 |m: &mut GridMeta| { &mut m.fields },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockMeta>>(
-                "blocks",
-                |m: &GridMeta| { &m.blocks },
-                |m: &mut GridMeta| { &mut m.blocks },
+                "block_metas",
+                |m: &GridMeta| { &m.block_metas },
+                |m: &mut GridMeta| { &mut m.block_metas },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridMeta>(
                 "GridMeta",
@@ -263,7 +263,7 @@ impl ::protobuf::Clear for GridMeta {
     fn clear(&mut self) {
         self.grid_id.clear();
         self.fields.clear();
-        self.blocks.clear();
+        self.block_metas.clear();
         self.unknown_fields.clear();
     }
 }
@@ -510,7 +510,7 @@ impl ::protobuf::reflect::ProtobufValue for GridBlockMeta {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct GridBlockMetaData {
+pub struct GridBlockMetaSerde {
     // message fields
     pub block_id: ::std::string::String,
     pub row_metas: ::protobuf::RepeatedField<RowMeta>,
@@ -519,14 +519,14 @@ pub struct GridBlockMetaData {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a GridBlockMetaData {
-    fn default() -> &'a GridBlockMetaData {
-        <GridBlockMetaData as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a GridBlockMetaSerde {
+    fn default() -> &'a GridBlockMetaSerde {
+        <GridBlockMetaSerde as ::protobuf::Message>::default_instance()
     }
 }
 
-impl GridBlockMetaData {
-    pub fn new() -> GridBlockMetaData {
+impl GridBlockMetaSerde {
+    pub fn new() -> GridBlockMetaSerde {
         ::std::default::Default::default()
     }
 
@@ -582,7 +582,7 @@ impl GridBlockMetaData {
     }
 }
 
-impl ::protobuf::Message for GridBlockMetaData {
+impl ::protobuf::Message for GridBlockMetaSerde {
     fn is_initialized(&self) -> bool {
         for v in &self.row_metas {
             if !v.is_initialized() {
@@ -665,8 +665,8 @@ impl ::protobuf::Message for GridBlockMetaData {
         Self::descriptor_static()
     }
 
-    fn new() -> GridBlockMetaData {
-        GridBlockMetaData::new()
+    fn new() -> GridBlockMetaSerde {
+        GridBlockMetaSerde::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -675,29 +675,29 @@ impl ::protobuf::Message for GridBlockMetaData {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "block_id",
-                |m: &GridBlockMetaData| { &m.block_id },
-                |m: &mut GridBlockMetaData| { &mut m.block_id },
+                |m: &GridBlockMetaSerde| { &m.block_id },
+                |m: &mut GridBlockMetaSerde| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowMeta>>(
                 "row_metas",
-                |m: &GridBlockMetaData| { &m.row_metas },
-                |m: &mut GridBlockMetaData| { &mut m.row_metas },
+                |m: &GridBlockMetaSerde| { &m.row_metas },
+                |m: &mut GridBlockMetaSerde| { &mut m.row_metas },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlockMetaData>(
-                "GridBlockMetaData",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlockMetaSerde>(
+                "GridBlockMetaSerde",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static GridBlockMetaData {
-        static instance: ::protobuf::rt::LazyV2<GridBlockMetaData> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(GridBlockMetaData::new)
+    fn default_instance() -> &'static GridBlockMetaSerde {
+        static instance: ::protobuf::rt::LazyV2<GridBlockMetaSerde> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(GridBlockMetaSerde::new)
     }
 }
 
-impl ::protobuf::Clear for GridBlockMetaData {
+impl ::protobuf::Clear for GridBlockMetaSerde {
     fn clear(&mut self) {
         self.block_id.clear();
         self.row_metas.clear();
@@ -705,13 +705,13 @@ impl ::protobuf::Clear for GridBlockMetaData {
     }
 }
 
-impl ::std::fmt::Debug for GridBlockMetaData {
+impl ::std::fmt::Debug for GridBlockMetaSerde {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for GridBlockMetaData {
+impl ::protobuf::reflect::ProtobufValue for GridBlockMetaSerde {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -3119,8 +3119,8 @@ impl ::protobuf::reflect::ProtobufValue for CellMetaChangeset {
 pub struct BuildGridContext {
     // message fields
     pub field_metas: ::protobuf::RepeatedField<FieldMeta>,
-    pub grid_block: ::protobuf::SingularPtrField<GridBlockMeta>,
-    pub grid_block_meta_data: ::protobuf::SingularPtrField<GridBlockMetaData>,
+    pub block_metas: ::protobuf::SingularPtrField<GridBlockMeta>,
+    pub block_meta_data: ::protobuf::SingularPtrField<GridBlockMetaSerde>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -3162,70 +3162,70 @@ impl BuildGridContext {
         ::std::mem::replace(&mut self.field_metas, ::protobuf::RepeatedField::new())
     }
 
-    // .GridBlockMeta grid_block = 2;
+    // .GridBlockMeta block_metas = 2;
 
 
-    pub fn get_grid_block(&self) -> &GridBlockMeta {
-        self.grid_block.as_ref().unwrap_or_else(|| <GridBlockMeta as ::protobuf::Message>::default_instance())
+    pub fn get_block_metas(&self) -> &GridBlockMeta {
+        self.block_metas.as_ref().unwrap_or_else(|| <GridBlockMeta as ::protobuf::Message>::default_instance())
     }
-    pub fn clear_grid_block(&mut self) {
-        self.grid_block.clear();
+    pub fn clear_block_metas(&mut self) {
+        self.block_metas.clear();
     }
 
-    pub fn has_grid_block(&self) -> bool {
-        self.grid_block.is_some()
+    pub fn has_block_metas(&self) -> bool {
+        self.block_metas.is_some()
     }
 
     // Param is passed by value, moved
-    pub fn set_grid_block(&mut self, v: GridBlockMeta) {
-        self.grid_block = ::protobuf::SingularPtrField::some(v);
+    pub fn set_block_metas(&mut self, v: GridBlockMeta) {
+        self.block_metas = ::protobuf::SingularPtrField::some(v);
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_grid_block(&mut self) -> &mut GridBlockMeta {
-        if self.grid_block.is_none() {
-            self.grid_block.set_default();
+    pub fn mut_block_metas(&mut self) -> &mut GridBlockMeta {
+        if self.block_metas.is_none() {
+            self.block_metas.set_default();
         }
-        self.grid_block.as_mut().unwrap()
+        self.block_metas.as_mut().unwrap()
     }
 
     // Take field
-    pub fn take_grid_block(&mut self) -> GridBlockMeta {
-        self.grid_block.take().unwrap_or_else(|| GridBlockMeta::new())
+    pub fn take_block_metas(&mut self) -> GridBlockMeta {
+        self.block_metas.take().unwrap_or_else(|| GridBlockMeta::new())
     }
 
-    // .GridBlockMetaData grid_block_meta_data = 3;
+    // .GridBlockMetaSerde block_meta_data = 3;
 
 
-    pub fn get_grid_block_meta_data(&self) -> &GridBlockMetaData {
-        self.grid_block_meta_data.as_ref().unwrap_or_else(|| <GridBlockMetaData as ::protobuf::Message>::default_instance())
+    pub fn get_block_meta_data(&self) -> &GridBlockMetaSerde {
+        self.block_meta_data.as_ref().unwrap_or_else(|| <GridBlockMetaSerde as ::protobuf::Message>::default_instance())
     }
-    pub fn clear_grid_block_meta_data(&mut self) {
-        self.grid_block_meta_data.clear();
+    pub fn clear_block_meta_data(&mut self) {
+        self.block_meta_data.clear();
     }
 
-    pub fn has_grid_block_meta_data(&self) -> bool {
-        self.grid_block_meta_data.is_some()
+    pub fn has_block_meta_data(&self) -> bool {
+        self.block_meta_data.is_some()
     }
 
     // Param is passed by value, moved
-    pub fn set_grid_block_meta_data(&mut self, v: GridBlockMetaData) {
-        self.grid_block_meta_data = ::protobuf::SingularPtrField::some(v);
+    pub fn set_block_meta_data(&mut self, v: GridBlockMetaSerde) {
+        self.block_meta_data = ::protobuf::SingularPtrField::some(v);
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_grid_block_meta_data(&mut self) -> &mut GridBlockMetaData {
-        if self.grid_block_meta_data.is_none() {
-            self.grid_block_meta_data.set_default();
+    pub fn mut_block_meta_data(&mut self) -> &mut GridBlockMetaSerde {
+        if self.block_meta_data.is_none() {
+            self.block_meta_data.set_default();
         }
-        self.grid_block_meta_data.as_mut().unwrap()
+        self.block_meta_data.as_mut().unwrap()
     }
 
     // Take field
-    pub fn take_grid_block_meta_data(&mut self) -> GridBlockMetaData {
-        self.grid_block_meta_data.take().unwrap_or_else(|| GridBlockMetaData::new())
+    pub fn take_block_meta_data(&mut self) -> GridBlockMetaSerde {
+        self.block_meta_data.take().unwrap_or_else(|| GridBlockMetaSerde::new())
     }
 }
 
@@ -3236,12 +3236,12 @@ impl ::protobuf::Message for BuildGridContext {
                 return false;
             }
         };
-        for v in &self.grid_block {
+        for v in &self.block_metas {
             if !v.is_initialized() {
                 return false;
             }
         };
-        for v in &self.grid_block_meta_data {
+        for v in &self.block_meta_data {
             if !v.is_initialized() {
                 return false;
             }
@@ -3257,10 +3257,10 @@ impl ::protobuf::Message for BuildGridContext {
                     ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.field_metas)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.grid_block)?;
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.block_metas)?;
                 },
                 3 => {
-                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.grid_block_meta_data)?;
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.block_meta_data)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -3278,11 +3278,11 @@ impl ::protobuf::Message for BuildGridContext {
             let len = value.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         };
-        if let Some(ref v) = self.grid_block.as_ref() {
+        if let Some(ref v) = self.block_metas.as_ref() {
             let len = v.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         }
-        if let Some(ref v) = self.grid_block_meta_data.as_ref() {
+        if let Some(ref v) = self.block_meta_data.as_ref() {
             let len = v.compute_size();
             my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         }
@@ -3297,12 +3297,12 @@ impl ::protobuf::Message for BuildGridContext {
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
         };
-        if let Some(ref v) = self.grid_block.as_ref() {
+        if let Some(ref v) = self.block_metas.as_ref() {
             os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
         }
-        if let Some(ref v) = self.grid_block_meta_data.as_ref() {
+        if let Some(ref v) = self.block_meta_data.as_ref() {
             os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
             os.write_raw_varint32(v.get_cached_size())?;
             v.write_to_with_cached_sizes(os)?;
@@ -3351,14 +3351,14 @@ impl ::protobuf::Message for BuildGridContext {
                 |m: &mut BuildGridContext| { &mut m.field_metas },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockMeta>>(
-                "grid_block",
-                |m: &BuildGridContext| { &m.grid_block },
-                |m: &mut BuildGridContext| { &mut m.grid_block },
+                "block_metas",
+                |m: &BuildGridContext| { &m.block_metas },
+                |m: &mut BuildGridContext| { &mut m.block_metas },
             ));
-            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockMetaData>>(
-                "grid_block_meta_data",
-                |m: &BuildGridContext| { &m.grid_block_meta_data },
-                |m: &mut BuildGridContext| { &mut m.grid_block_meta_data },
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockMetaSerde>>(
+                "block_meta_data",
+                |m: &BuildGridContext| { &m.block_meta_data },
+                |m: &mut BuildGridContext| { &mut m.block_meta_data },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<BuildGridContext>(
                 "BuildGridContext",
@@ -3377,8 +3377,8 @@ impl ::protobuf::Message for BuildGridContext {
 impl ::protobuf::Clear for BuildGridContext {
     fn clear(&mut self) {
         self.field_metas.clear();
-        self.grid_block.clear();
-        self.grid_block_meta_data.clear();
+        self.block_metas.clear();
+        self.block_meta_data.clear();
         self.unknown_fields.clear();
     }
 }
@@ -3458,22 +3458,22 @@ impl ::protobuf::reflect::ProtobufValue for FieldType {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\nmeta.proto\"o\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
+    \n\nmeta.proto\"x\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
     \x06gridId\x12\"\n\x06fields\x18\x02\x20\x03(\x0b2\n.FieldMetaR\x06field\
-    s\x12&\n\x06blocks\x18\x03\x20\x03(\x0b2\x0e.GridBlockMetaR\x06blocks\"o\
-    \n\rGridBlockMeta\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\
-    \x12&\n\x0fstart_row_index\x18\x02\x20\x01(\x05R\rstartRowIndex\x12\x1b\
-    \n\trow_count\x18\x03\x20\x01(\x05R\x08rowCount\"U\n\x11GridBlockMetaDat\
-    a\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12%\n\trow_metas\
-    \x18\x02\x20\x03(\x0b2\x08.RowMetaR\x08rowMetas\"\xdf\x01\n\tFieldMeta\
-    \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\
-    \x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12)\n\nf\
-    ield_type\x18\x04\x20\x01(\x0e2\n.FieldTypeR\tfieldType\x12\x16\n\x06fro\
-    zen\x18\x05\x20\x01(\x08R\x06frozen\x12\x1e\n\nvisibility\x18\x06\x20\
-    \x01(\x08R\nvisibility\x12\x14\n\x05width\x18\x07\x20\x01(\x05R\x05width\
-    \x12!\n\x0ctype_options\x18\x08\x20\x01(\tR\x0btypeOptions\"\xfd\x02\n\
-    \x0eFieldChangeset\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\
-    \x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\
+    s\x12/\n\x0bblock_metas\x18\x03\x20\x03(\x0b2\x0e.GridBlockMetaR\nblockM\
+    etas\"o\n\rGridBlockMeta\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07bl\
+    ockId\x12&\n\x0fstart_row_index\x18\x02\x20\x01(\x05R\rstartRowIndex\x12\
+    \x1b\n\trow_count\x18\x03\x20\x01(\x05R\x08rowCount\"V\n\x12GridBlockMet\
+    aSerde\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12%\n\trow_\
+    metas\x18\x02\x20\x03(\x0b2\x08.RowMetaR\x08rowMetas\"\xdf\x01\n\tFieldM\
+    eta\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\
+    \x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12)\
+    \n\nfield_type\x18\x04\x20\x01(\x0e2\n.FieldTypeR\tfieldType\x12\x16\n\
+    \x06frozen\x18\x05\x20\x01(\x08R\x06frozen\x12\x1e\n\nvisibility\x18\x06\
+    \x20\x01(\x08R\nvisibility\x12\x14\n\x05width\x18\x07\x20\x01(\x05R\x05w\
+    idth\x12!\n\x0ctype_options\x18\x08\x20\x01(\tR\x0btypeOptions\"\xfd\x02\
+    \n\x0eFieldChangeset\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldI\
+    d\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\
     \x03\x20\x01(\tH\x01R\x04desc\x12+\n\nfield_type\x18\x04\x20\x01(\x0e2\n\
     .FieldTypeH\x02R\tfieldType\x12\x18\n\x06frozen\x18\x05\x20\x01(\x08H\
     \x03R\x06frozen\x12\x20\n\nvisibility\x18\x06\x20\x01(\x08H\x04R\nvisibi\
@@ -3501,14 +3501,13 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x11CellMetaChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\
     \x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\
     \x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0R\
-    \x04dataB\r\n\x0bone_of_data\"\xb3\x01\n\x10BuildGridContext\x12+\n\x0bf\
-    ield_metas\x18\x01\x20\x03(\x0b2\n.FieldMetaR\nfieldMetas\x12-\n\ngrid_b\
-    lock\x18\x02\x20\x01(\x0b2\x0e.GridBlockMetaR\tgridBlock\x12C\n\x14grid_\
-    block_meta_data\x18\x03\x20\x01(\x0b2\x12.GridBlockMetaDataR\x11gridBloc\
-    kMetaData*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\
-    \x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\
-    \x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06prot\
-    o3\
+    \x04dataB\r\n\x0bone_of_data\"\xad\x01\n\x10BuildGridContext\x12+\n\x0bf\
+    ield_metas\x18\x01\x20\x03(\x0b2\n.FieldMetaR\nfieldMetas\x12/\n\x0bbloc\
+    k_metas\x18\x02\x20\x01(\x0b2\x0e.GridBlockMetaR\nblockMetas\x12;\n\x0fb\
+    lock_meta_data\x18\x03\x20\x01(\x0b2\x13.GridBlockMetaSerdeR\rblockMetaD\
+    ata*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\
+    \x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\
+    \n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 15 - 4
shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

@@ -4,7 +4,7 @@ import "meta.proto";
 message Grid {
     string id = 1;
     repeated FieldOrder field_orders = 2;
-    repeated GridBlockMeta blocks = 3;
+    repeated GridBlockOrder block_orders = 3;
 }
 message Field {
     string id = 1;
@@ -36,12 +36,18 @@ message Row {
     map<string, Cell> cell_by_field_id = 2;
     int32 height = 3;
 }
+message RepeatedRow {
+    repeated Row items = 1;
+}
 message RepeatedGridBlock {
     repeated GridBlock items = 1;
 }
+message GridBlockOrder {
+    string block_id = 1;
+}
 message GridBlock {
     string block_id = 1;
-    repeated Row rows = 2;
+    repeated string row_ids = 2;
 }
 message Cell {
     string field_id = 1;
@@ -61,7 +67,7 @@ message GridBlockId {
 }
 message CreateRowPayload {
     string grid_id = 1;
-    oneof one_of_upper_row_id { string upper_row_id = 2; };
+    oneof one_of_start_row_id { string start_row_id = 2; };
 }
 message QueryFieldPayload {
     string grid_id = 1;
@@ -69,5 +75,10 @@ message QueryFieldPayload {
 }
 message QueryGridBlocksPayload {
     string grid_id = 1;
-    repeated GridBlockMeta blocks = 2;
+    repeated GridBlockOrder block_orders = 2;
+}
+message QueryRowPayload {
+    string grid_id = 1;
+    string block_id = 2;
+    string row_id = 3;
 }

+ 4 - 4
shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto

@@ -3,14 +3,14 @@ syntax = "proto3";
 message GridMeta {
     string grid_id = 1;
     repeated FieldMeta fields = 2;
-    repeated GridBlockMeta blocks = 3;
+    repeated GridBlockMeta block_metas = 3;
 }
 message GridBlockMeta {
     string block_id = 1;
     int32 start_row_index = 2;
     int32 row_count = 3;
 }
-message GridBlockMetaData {
+message GridBlockMetaSerde {
     string block_id = 1;
     repeated RowMeta row_metas = 2;
 }
@@ -63,8 +63,8 @@ message CellMetaChangeset {
 }
 message BuildGridContext {
     repeated FieldMeta field_metas = 1;
-    GridBlockMeta grid_block = 2;
-    GridBlockMetaData grid_block_meta_data = 3;
+    GridBlockMeta block_metas = 2;
+    GridBlockMetaSerde block_meta_data = 3;
 }
 enum FieldType {
     RichText = 0;

+ 2 - 2
shared-lib/flowy-grid-data-model/tests/serde_test.rs

@@ -7,7 +7,7 @@ fn grid_serde_test() {
     let grid = GridMeta {
         grid_id,
         fields,
-        blocks: vec![],
+        block_metas: vec![],
     };
 
     let grid_1_json = serde_json::to_string(&grid).unwrap();
@@ -24,7 +24,7 @@ fn grid_default_serde_test() {
     let grid = GridMeta {
         grid_id,
         fields: vec![],
-        blocks: vec![],
+        block_metas: vec![],
     };
 
     let json = serde_json::to_string(&grid).unwrap();