Browse Source

chore: rename struct

appflowy 3 years ago
parent
commit
7ac6a1dc89
72 changed files with 1788 additions and 785 deletions
  1. 1 0
      frontend/app_flowy/lib/startup/home_deps_resolver.dart
  2. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart
  3. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/date_cell_bloc.dart
  4. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart
  5. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_cell_bloc.dart
  6. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/text_cell_bloc.dart
  7. 1 1
      frontend/app_flowy/lib/workspace/application/grid/column_bloc.dart
  8. 1 0
      frontend/app_flowy/lib/workspace/application/grid/data.dart
  9. 1 0
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  10. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart
  11. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart
  12. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_builder.dart
  13. 0 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart
  14. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/checkbox_cell.dart
  15. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/date_cell.dart
  16. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/number_cell.dart
  17. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/selection_cell.dart
  18. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/text_cell.dart
  19. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart
  20. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart
  21. 0 1
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart
  22. 1 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/protobuf.dart
  23. 412 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pb.dart
  24. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbenum.dart
  25. 78 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbjson.dart
  26. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbserver.dart
  27. 97 22
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart
  28. 19 7
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart
  29. 10 10
      frontend/rust-lib/flowy-block/src/editor.rs
  30. 11 11
      frontend/rust-lib/flowy-block/src/event_handler.rs
  31. 2 2
      frontend/rust-lib/flowy-block/src/event_map.rs
  32. 7 5
      frontend/rust-lib/flowy-block/src/lib.rs
  33. 40 49
      frontend/rust-lib/flowy-block/src/manager.rs
  34. 3 3
      frontend/rust-lib/flowy-block/src/queue.rs
  35. 10 10
      frontend/rust-lib/flowy-block/src/web_socket.rs
  36. 3 3
      frontend/rust-lib/flowy-block/tests/document/edit_script.rs
  37. 2 1
      frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql
  38. 9 0
      frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql
  39. 2 2
      frontend/rust-lib/flowy-folder/src/services/view/controller.rs
  40. 3 3
      frontend/rust-lib/flowy-folder/tests/workspace/helper.rs
  41. 2 2
      frontend/rust-lib/flowy-folder/tests/workspace/script.rs
  42. 1 1
      frontend/rust-lib/flowy-grid/src/services/grid_builder.rs
  43. 13 7
      frontend/rust-lib/flowy-net/src/http_server/document.rs
  44. 3 3
      frontend/rust-lib/flowy-net/src/local_server/persistence.rs
  45. 5 5
      frontend/rust-lib/flowy-net/src/local_server/server.rs
  46. 19 16
      frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
  47. 2 2
      frontend/rust-lib/flowy-sdk/src/deps_resolve/mod.rs
  48. 10 10
      frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs
  49. 20 8
      frontend/rust-lib/flowy-sdk/src/lib.rs
  50. 12 6
      frontend/rust-lib/flowy-sdk/src/module.rs
  51. 0 0
      frontend/rust-lib/flowy-sync/src/cache/disk/folder_rev_impl.rs
  52. 0 0
      frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs
  53. 8 3
      frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs
  54. 3 3
      frontend/rust-lib/flowy-sync/src/cache/disk/text_block_rev_impl.rs
  55. 2 325
      frontend/rust-lib/flowy-sync/src/cache/mod.rs
  56. 2 0
      frontend/rust-lib/flowy-sync/src/lib.rs
  57. 322 0
      frontend/rust-lib/flowy-sync/src/rev_persistence.rs
  58. 115 0
      shared-lib/flowy-collaboration/src/client_grid/block_pad.rs
  59. 10 10
      shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs
  60. 2 0
      shared-lib/flowy-collaboration/src/client_grid/mod.rs
  61. 1 1
      shared-lib/flowy-collaboration/src/entities/mod.rs
  62. 15 15
      shared-lib/flowy-collaboration/src/entities/text_block_info.rs
  63. 3 3
      shared-lib/flowy-collaboration/src/protobuf/model/mod.rs
  64. 122 122
      shared-lib/flowy-collaboration/src/protobuf/model/text_block_info.rs
  65. 5 5
      shared-lib/flowy-collaboration/src/protobuf/proto/text_block_info.proto
  66. 5 5
      shared-lib/flowy-collaboration/src/server_document/document_manager.rs
  67. 7 7
      shared-lib/flowy-collaboration/src/util.rs
  68. 1 3
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  69. 14 3
      shared-lib/flowy-grid-data-model/src/entities/meta.rs
  70. 308 77
      shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs
  71. 7 2
      shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto
  72. 5 5
      shared-lib/flowy-grid-data-model/tests/serde_test.rs

+ 1 - 0
frontend/app_flowy/lib/startup/home_deps_resolver.dart

@@ -13,6 +13,7 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.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:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
 import 'package:get_it/get_it.dart';
 

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/date_cell_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_cell_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/text_cell_bloc.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

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

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

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

@@ -1,5 +1,6 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:equatable/equatable.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 
 class GridInfo {
   List<Row> rows;

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

@@ -3,6 +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';
 
 class GridService {
   Future<Either<Grid, FlowyError>> openGrid({required String gridId}) async {

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

@@ -6,7 +6,7 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
 import 'package:flowy_infra_ui/widget/error_page.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:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter/material.dart';
 import 'package:styled_widget/styled_widget.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 
 import 'sizes.dart';
 

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

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/widgets.dart';
 import 'checkbox_cell.dart';
 import 'date_cell.dart';

+ 0 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:flowy_infra/theme.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

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

@@ -1,6 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

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

@@ -1,6 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/date_cell_bloc.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

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

@@ -1,6 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/number_cell_bloc.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

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

@@ -1,6 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/prelude.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/material.dart';
 
 class SingleSelectCell extends StatefulWidget {

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

@@ -1,6 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/text_cell_bloc.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart

@@ -5,7 +5,7 @@ import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart

@@ -2,7 +2,7 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.d
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 0 - 1
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart

@@ -22,7 +22,6 @@ import 'package:flowy_sdk/protobuf/flowy-user-data-model/protobuf.dart';
 import 'package:flowy_sdk/protobuf/dart-ffi/protobuf.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/protobuf.dart';
 import 'package:flowy_sdk/protobuf/flowy-block/protobuf.dart';
-import 'package:flowy_sdk/protobuf/flowy-collaboration/protobuf.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
 
 // ignore: unused_import

+ 1 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/protobuf.dart

@@ -1,5 +1,5 @@
 // Auto-generated, do not edit 
 export './folder_info.pb.dart';
 export './ws_data.pb.dart';
+export './text_block_info.pb.dart';
 export './revision.pb.dart';
-export './document_info.pb.dart';

+ 412 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pb.dart

@@ -0,0 +1,412 @@
+///
+//  Generated code. Do not modify.
+//  source: text_block_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:fixnum/fixnum.dart' as $fixnum;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'revision.pb.dart' as $0;
+
+class CreateTextBlockParams extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateTextBlockParams', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
+    ..aOM<$0.RepeatedRevision>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revisions', subBuilder: $0.RepeatedRevision.create)
+    ..hasRequiredFields = false
+  ;
+
+  CreateTextBlockParams._() : super();
+  factory CreateTextBlockParams({
+    $core.String? id,
+    $0.RepeatedRevision? revisions,
+  }) {
+    final _result = create();
+    if (id != null) {
+      _result.id = id;
+    }
+    if (revisions != null) {
+      _result.revisions = revisions;
+    }
+    return _result;
+  }
+  factory CreateTextBlockParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CreateTextBlockParams.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')
+  CreateTextBlockParams clone() => CreateTextBlockParams()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CreateTextBlockParams copyWith(void Function(CreateTextBlockParams) updates) => super.copyWith((message) => updates(message as CreateTextBlockParams)) as CreateTextBlockParams; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CreateTextBlockParams create() => CreateTextBlockParams._();
+  CreateTextBlockParams createEmptyInstance() => create();
+  static $pb.PbList<CreateTextBlockParams> createRepeated() => $pb.PbList<CreateTextBlockParams>();
+  @$core.pragma('dart2js:noInline')
+  static CreateTextBlockParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateTextBlockParams>(create);
+  static CreateTextBlockParams? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get id => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set id($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $0.RepeatedRevision get revisions => $_getN(1);
+  @$pb.TagNumber(2)
+  set revisions($0.RepeatedRevision v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasRevisions() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearRevisions() => clearField(2);
+  @$pb.TagNumber(2)
+  $0.RepeatedRevision ensureRevisions() => $_ensure(1);
+}
+
+class TextBlockInfo extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TextBlockInfo', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'text')
+    ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revId')
+    ..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'baseRevId')
+    ..hasRequiredFields = false
+  ;
+
+  TextBlockInfo._() : super();
+  factory TextBlockInfo({
+    $core.String? blockId,
+    $core.String? text,
+    $fixnum.Int64? revId,
+    $fixnum.Int64? baseRevId,
+  }) {
+    final _result = create();
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    if (text != null) {
+      _result.text = text;
+    }
+    if (revId != null) {
+      _result.revId = revId;
+    }
+    if (baseRevId != null) {
+      _result.baseRevId = baseRevId;
+    }
+    return _result;
+  }
+  factory TextBlockInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory TextBlockInfo.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')
+  TextBlockInfo clone() => TextBlockInfo()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  TextBlockInfo copyWith(void Function(TextBlockInfo) updates) => super.copyWith((message) => updates(message as TextBlockInfo)) as TextBlockInfo; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static TextBlockInfo create() => TextBlockInfo._();
+  TextBlockInfo createEmptyInstance() => create();
+  static $pb.PbList<TextBlockInfo> createRepeated() => $pb.PbList<TextBlockInfo>();
+  @$core.pragma('dart2js:noInline')
+  static TextBlockInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<TextBlockInfo>(create);
+  static TextBlockInfo? _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);
+
+  @$pb.TagNumber(2)
+  $core.String get text => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set text($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasText() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearText() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get revId => $_getI64(2);
+  @$pb.TagNumber(3)
+  set revId($fixnum.Int64 v) { $_setInt64(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRevId() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearRevId() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $fixnum.Int64 get baseRevId => $_getI64(3);
+  @$pb.TagNumber(4)
+  set baseRevId($fixnum.Int64 v) { $_setInt64(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasBaseRevId() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearBaseRevId() => clearField(4);
+}
+
+class ResetTextBlockParams extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ResetTextBlockParams', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..aOM<$0.RepeatedRevision>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revisions', subBuilder: $0.RepeatedRevision.create)
+    ..hasRequiredFields = false
+  ;
+
+  ResetTextBlockParams._() : super();
+  factory ResetTextBlockParams({
+    $core.String? blockId,
+    $0.RepeatedRevision? revisions,
+  }) {
+    final _result = create();
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    if (revisions != null) {
+      _result.revisions = revisions;
+    }
+    return _result;
+  }
+  factory ResetTextBlockParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ResetTextBlockParams.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')
+  ResetTextBlockParams clone() => ResetTextBlockParams()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ResetTextBlockParams copyWith(void Function(ResetTextBlockParams) updates) => super.copyWith((message) => updates(message as ResetTextBlockParams)) as ResetTextBlockParams; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ResetTextBlockParams create() => ResetTextBlockParams._();
+  ResetTextBlockParams createEmptyInstance() => create();
+  static $pb.PbList<ResetTextBlockParams> createRepeated() => $pb.PbList<ResetTextBlockParams>();
+  @$core.pragma('dart2js:noInline')
+  static ResetTextBlockParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ResetTextBlockParams>(create);
+  static ResetTextBlockParams? _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);
+
+  @$pb.TagNumber(2)
+  $0.RepeatedRevision get revisions => $_getN(1);
+  @$pb.TagNumber(2)
+  set revisions($0.RepeatedRevision v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasRevisions() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearRevisions() => clearField(2);
+  @$pb.TagNumber(2)
+  $0.RepeatedRevision ensureRevisions() => $_ensure(1);
+}
+
+class TextBlockDelta extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TextBlockDelta', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deltaStr')
+    ..hasRequiredFields = false
+  ;
+
+  TextBlockDelta._() : super();
+  factory TextBlockDelta({
+    $core.String? blockId,
+    $core.String? deltaStr,
+  }) {
+    final _result = create();
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    if (deltaStr != null) {
+      _result.deltaStr = deltaStr;
+    }
+    return _result;
+  }
+  factory TextBlockDelta.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory TextBlockDelta.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')
+  TextBlockDelta clone() => TextBlockDelta()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  TextBlockDelta copyWith(void Function(TextBlockDelta) updates) => super.copyWith((message) => updates(message as TextBlockDelta)) as TextBlockDelta; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static TextBlockDelta create() => TextBlockDelta._();
+  TextBlockDelta createEmptyInstance() => create();
+  static $pb.PbList<TextBlockDelta> createRepeated() => $pb.PbList<TextBlockDelta>();
+  @$core.pragma('dart2js:noInline')
+  static TextBlockDelta getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<TextBlockDelta>(create);
+  static TextBlockDelta? _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);
+
+  @$pb.TagNumber(2)
+  $core.String get deltaStr => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set deltaStr($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasDeltaStr() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearDeltaStr() => clearField(2);
+}
+
+class NewDocUser extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NewDocUser', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'userId')
+    ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'docId')
+    ..hasRequiredFields = false
+  ;
+
+  NewDocUser._() : super();
+  factory NewDocUser({
+    $core.String? userId,
+    $fixnum.Int64? revId,
+    $core.String? docId,
+  }) {
+    final _result = create();
+    if (userId != null) {
+      _result.userId = userId;
+    }
+    if (revId != null) {
+      _result.revId = revId;
+    }
+    if (docId != null) {
+      _result.docId = docId;
+    }
+    return _result;
+  }
+  factory NewDocUser.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory NewDocUser.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')
+  NewDocUser clone() => NewDocUser()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  NewDocUser copyWith(void Function(NewDocUser) updates) => super.copyWith((message) => updates(message as NewDocUser)) as NewDocUser; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static NewDocUser create() => NewDocUser._();
+  NewDocUser createEmptyInstance() => create();
+  static $pb.PbList<NewDocUser> createRepeated() => $pb.PbList<NewDocUser>();
+  @$core.pragma('dart2js:noInline')
+  static NewDocUser getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<NewDocUser>(create);
+  static NewDocUser? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get userId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set userId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasUserId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearUserId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get revId => $_getI64(1);
+  @$pb.TagNumber(2)
+  set revId($fixnum.Int64 v) { $_setInt64(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasRevId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearRevId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get docId => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set docId($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasDocId() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearDocId() => clearField(3);
+}
+
+class TextBlockId extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TextBlockId', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'value')
+    ..hasRequiredFields = false
+  ;
+
+  TextBlockId._() : super();
+  factory TextBlockId({
+    $core.String? value,
+  }) {
+    final _result = create();
+    if (value != null) {
+      _result.value = value;
+    }
+    return _result;
+  }
+  factory TextBlockId.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory TextBlockId.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')
+  TextBlockId clone() => TextBlockId()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  TextBlockId copyWith(void Function(TextBlockId) updates) => super.copyWith((message) => updates(message as TextBlockId)) as TextBlockId; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static TextBlockId create() => TextBlockId._();
+  TextBlockId createEmptyInstance() => create();
+  static $pb.PbList<TextBlockId> createRepeated() => $pb.PbList<TextBlockId>();
+  @$core.pragma('dart2js:noInline')
+  static TextBlockId getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<TextBlockId>(create);
+  static TextBlockId? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get value => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set value($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasValue() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearValue() => clearField(1);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: text_block_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 78 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbjson.dart

@@ -0,0 +1,78 @@
+///
+//  Generated code. Do not modify.
+//  source: text_block_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use createTextBlockParamsDescriptor instead')
+const CreateTextBlockParams$json = const {
+  '1': 'CreateTextBlockParams',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'revisions', '3': 2, '4': 1, '5': 11, '6': '.RepeatedRevision', '10': 'revisions'},
+  ],
+};
+
+/// Descriptor for `CreateTextBlockParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List createTextBlockParamsDescriptor = $convert.base64Decode('ChVDcmVhdGVUZXh0QmxvY2tQYXJhbXMSDgoCaWQYASABKAlSAmlkEi8KCXJldmlzaW9ucxgCIAEoCzIRLlJlcGVhdGVkUmV2aXNpb25SCXJldmlzaW9ucw==');
+@$core.Deprecated('Use textBlockInfoDescriptor instead')
+const TextBlockInfo$json = const {
+  '1': 'TextBlockInfo',
+  '2': const [
+    const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
+    const {'1': 'text', '3': 2, '4': 1, '5': 9, '10': 'text'},
+    const {'1': 'rev_id', '3': 3, '4': 1, '5': 3, '10': 'revId'},
+    const {'1': 'base_rev_id', '3': 4, '4': 1, '5': 3, '10': 'baseRevId'},
+  ],
+};
+
+/// Descriptor for `TextBlockInfo`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List textBlockInfoDescriptor = $convert.base64Decode('Cg1UZXh0QmxvY2tJbmZvEhkKCGJsb2NrX2lkGAEgASgJUgdibG9ja0lkEhIKBHRleHQYAiABKAlSBHRleHQSFQoGcmV2X2lkGAMgASgDUgVyZXZJZBIeCgtiYXNlX3Jldl9pZBgEIAEoA1IJYmFzZVJldklk');
+@$core.Deprecated('Use resetTextBlockParamsDescriptor instead')
+const ResetTextBlockParams$json = const {
+  '1': 'ResetTextBlockParams',
+  '2': const [
+    const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
+    const {'1': 'revisions', '3': 2, '4': 1, '5': 11, '6': '.RepeatedRevision', '10': 'revisions'},
+  ],
+};
+
+/// Descriptor for `ResetTextBlockParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List resetTextBlockParamsDescriptor = $convert.base64Decode('ChRSZXNldFRleHRCbG9ja1BhcmFtcxIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIvCglyZXZpc2lvbnMYAiABKAsyES5SZXBlYXRlZFJldmlzaW9uUglyZXZpc2lvbnM=');
+@$core.Deprecated('Use textBlockDeltaDescriptor instead')
+const TextBlockDelta$json = const {
+  '1': 'TextBlockDelta',
+  '2': const [
+    const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
+    const {'1': 'delta_str', '3': 2, '4': 1, '5': 9, '10': 'deltaStr'},
+  ],
+};
+
+/// Descriptor for `TextBlockDelta`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List textBlockDeltaDescriptor = $convert.base64Decode('Cg5UZXh0QmxvY2tEZWx0YRIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIbCglkZWx0YV9zdHIYAiABKAlSCGRlbHRhU3Ry');
+@$core.Deprecated('Use newDocUserDescriptor instead')
+const NewDocUser$json = const {
+  '1': 'NewDocUser',
+  '2': const [
+    const {'1': 'user_id', '3': 1, '4': 1, '5': 9, '10': 'userId'},
+    const {'1': 'rev_id', '3': 2, '4': 1, '5': 3, '10': 'revId'},
+    const {'1': 'doc_id', '3': 3, '4': 1, '5': 9, '10': 'docId'},
+  ],
+};
+
+/// Descriptor for `NewDocUser`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List newDocUserDescriptor = $convert.base64Decode('CgpOZXdEb2NVc2VyEhcKB3VzZXJfaWQYASABKAlSBnVzZXJJZBIVCgZyZXZfaWQYAiABKANSBXJldklkEhUKBmRvY19pZBgDIAEoCVIFZG9jSWQ=');
+@$core.Deprecated('Use textBlockIdDescriptor instead')
+const TextBlockId$json = const {
+  '1': 'TextBlockId',
+  '2': const [
+    const {'1': 'value', '3': 1, '4': 1, '5': 9, '10': 'value'},
+  ],
+};
+
+/// Descriptor for `TextBlockId`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List textBlockIdDescriptor = $convert.base64Decode('CgtUZXh0QmxvY2tJZBIUCgV2YWx1ZRgBIAEoCVIFdmFsdWU=');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-collaboration/text_block_info.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: text_block_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'text_block_info.pb.dart';
+

+ 97 - 22
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<Field>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fields', $pb.PbFieldType.PM, subBuilder: Field.create)
-    ..pc<RowMeta>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rows', $pb.PbFieldType.PM, subBuilder: RowMeta.create)
+    ..pc<Block>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blocks', $pb.PbFieldType.PM, subBuilder: Block.create)
     ..hasRequiredFields = false
   ;
 
@@ -25,7 +25,7 @@ class GridMeta extends $pb.GeneratedMessage {
   factory GridMeta({
     $core.String? gridId,
     $core.Iterable<Field>? fields,
-    $core.Iterable<RowMeta>? rows,
+    $core.Iterable<Block>? blocks,
   }) {
     final _result = create();
     if (gridId != null) {
@@ -34,8 +34,8 @@ class GridMeta extends $pb.GeneratedMessage {
     if (fields != null) {
       _result.fields.addAll(fields);
     }
-    if (rows != null) {
-      _result.rows.addAll(rows);
+    if (blocks != null) {
+      _result.blocks.addAll(blocks);
     }
     return _result;
   }
@@ -73,50 +73,55 @@ class GridMeta extends $pb.GeneratedMessage {
   $core.List<Field> get fields => $_getList(1);
 
   @$pb.TagNumber(3)
-  $core.List<RowMeta> get rows => $_getList(2);
+  $core.List<Block> get blocks => $_getList(2);
 }
 
-class GridBlock extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlock', createEmptyInstance: create)
+class Block extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Block', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
-    ..pc<RowMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rows', $pb.PbFieldType.PM, subBuilder: RowMeta.create)
+    ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startRowIndex', $pb.PbFieldType.O3)
+    ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowCount', $pb.PbFieldType.O3)
     ..hasRequiredFields = false
   ;
 
-  GridBlock._() : super();
-  factory GridBlock({
+  Block._() : super();
+  factory Block({
     $core.String? id,
-    $core.Iterable<RowMeta>? rows,
+    $core.int? startRowIndex,
+    $core.int? rowCount,
   }) {
     final _result = create();
     if (id != null) {
       _result.id = id;
     }
-    if (rows != null) {
-      _result.rows.addAll(rows);
+    if (startRowIndex != null) {
+      _result.startRowIndex = startRowIndex;
+    }
+    if (rowCount != null) {
+      _result.rowCount = rowCount;
     }
     return _result;
   }
-  factory GridBlock.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory GridBlock.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory Block.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory Block.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')
-  GridBlock clone() => GridBlock()..mergeFromMessage(this);
+  Block clone() => Block()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  GridBlock copyWith(void Function(GridBlock) updates) => super.copyWith((message) => updates(message as GridBlock)) as GridBlock; // ignore: deprecated_member_use
+  Block copyWith(void Function(Block) updates) => super.copyWith((message) => updates(message as Block)) as Block; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static GridBlock create() => GridBlock._();
-  GridBlock createEmptyInstance() => create();
-  static $pb.PbList<GridBlock> createRepeated() => $pb.PbList<GridBlock>();
+  static Block create() => Block._();
+  Block createEmptyInstance() => create();
+  static $pb.PbList<Block> createRepeated() => $pb.PbList<Block>();
   @$core.pragma('dart2js:noInline')
-  static GridBlock getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlock>(create);
-  static GridBlock? _defaultInstance;
+  static Block getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Block>(create);
+  static Block? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);
@@ -127,6 +132,76 @@ class GridBlock extends $pb.GeneratedMessage {
   @$pb.TagNumber(1)
   void clearId() => clearField(1);
 
+  @$pb.TagNumber(2)
+  $core.int get startRowIndex => $_getIZ(1);
+  @$pb.TagNumber(2)
+  set startRowIndex($core.int v) { $_setSignedInt32(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasStartRowIndex() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearStartRowIndex() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.int get rowCount => $_getIZ(2);
+  @$pb.TagNumber(3)
+  set rowCount($core.int v) { $_setSignedInt32(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRowCount() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearRowCount() => clearField(3);
+}
+
+class BlockMeta extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BlockMeta', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
+    ..pc<RowMeta>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rows', $pb.PbFieldType.PM, subBuilder: RowMeta.create)
+    ..hasRequiredFields = false
+  ;
+
+  BlockMeta._() : super();
+  factory BlockMeta({
+    $core.String? blockId,
+    $core.Iterable<RowMeta>? rows,
+  }) {
+    final _result = create();
+    if (blockId != null) {
+      _result.blockId = blockId;
+    }
+    if (rows != null) {
+      _result.rows.addAll(rows);
+    }
+    return _result;
+  }
+  factory BlockMeta.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory BlockMeta.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')
+  BlockMeta clone() => BlockMeta()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  BlockMeta copyWith(void Function(BlockMeta) updates) => super.copyWith((message) => updates(message as BlockMeta)) as BlockMeta; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static BlockMeta create() => BlockMeta._();
+  BlockMeta createEmptyInstance() => create();
+  static $pb.PbList<BlockMeta> createRepeated() => $pb.PbList<BlockMeta>();
+  @$core.pragma('dart2js:noInline')
+  static BlockMeta getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BlockMeta>(create);
+  static BlockMeta? _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);
+
   @$pb.TagNumber(2)
   $core.List<RowMeta> get rows => $_getList(1);
 }

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

@@ -29,23 +29,35 @@ 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': '.Field', '10': 'fields'},
-    const {'1': 'rows', '3': 3, '4': 3, '5': 11, '6': '.RowMeta', '10': 'rows'},
+    const {'1': 'blocks', '3': 3, '4': 3, '5': 11, '6': '.Block', '10': 'blocks'},
   ],
 };
 
 /// Descriptor for `GridMeta`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSHgoGZmllbGRzGAIgAygLMgYuRmllbGRSBmZpZWxkcxIcCgRyb3dzGAMgAygLMgguUm93TWV0YVIEcm93cw==');
-@$core.Deprecated('Use gridBlockDescriptor instead')
-const GridBlock$json = const {
-  '1': 'GridBlock',
+final $typed_data.Uint8List gridMetaDescriptor = $convert.base64Decode('CghHcmlkTWV0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSHgoGZmllbGRzGAIgAygLMgYuRmllbGRSBmZpZWxkcxIeCgZibG9ja3MYAyADKAsyBi5CbG9ja1IGYmxvY2tz');
+@$core.Deprecated('Use blockDescriptor instead')
+const Block$json = const {
+  '1': 'Block',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'start_row_index', '3': 2, '4': 1, '5': 5, '10': 'startRowIndex'},
+    const {'1': 'row_count', '3': 3, '4': 1, '5': 5, '10': 'rowCount'},
+  ],
+};
+
+/// Descriptor for `Block`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List blockDescriptor = $convert.base64Decode('CgVCbG9jaxIOCgJpZBgBIAEoCVICaWQSJgoPc3RhcnRfcm93X2luZGV4GAIgASgFUg1zdGFydFJvd0luZGV4EhsKCXJvd19jb3VudBgDIAEoBVIIcm93Q291bnQ=');
+@$core.Deprecated('Use blockMetaDescriptor instead')
+const BlockMeta$json = const {
+  '1': 'BlockMeta',
+  '2': const [
+    const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
     const {'1': 'rows', '3': 2, '4': 3, '5': 11, '6': '.RowMeta', '10': 'rows'},
   ],
 };
 
-/// Descriptor for `GridBlock`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSDgoCaWQYASABKAlSAmlkEhwKBHJvd3MYAiADKAsyCC5Sb3dNZXRhUgRyb3dz');
+/// Descriptor for `BlockMeta`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List blockMetaDescriptor = $convert.base64Decode('CglCbG9ja01ldGESGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSHAoEcm93cxgCIAMoCzIILlJvd01ldGFSBHJvd3M=');
 @$core.Deprecated('Use fieldDescriptor instead')
 const Field$json = const {
   '1': 'Field',

+ 10 - 10
frontend/rust-lib/flowy-block/src/block_editor.rs → frontend/rust-lib/flowy-block/src/editor.rs

@@ -3,12 +3,12 @@ use crate::web_socket::{make_block_ws_manager, EditorCommandSender};
 use crate::{
     errors::FlowyError,
     queue::{EditBlockQueue, EditorCommand},
-    BlockUser,
+    TextBlockUser,
 };
 use bytes::Bytes;
 use flowy_collaboration::entities::ws_data::ServerRevisionWSData;
 use flowy_collaboration::{
-    entities::{document_info::BlockInfo, revision::Revision},
+    entities::{revision::Revision, text_block_info::TextBlockInfo},
     errors::CollaborateResult,
     util::make_delta_from_revisions,
 };
@@ -24,7 +24,7 @@ use lib_ws::WSConnectState;
 use std::sync::Arc;
 use tokio::sync::{mpsc, oneshot};
 
-pub struct ClientBlockEditor {
+pub struct ClientTextBlockEditor {
     pub doc_id: String,
     #[allow(dead_code)]
     rev_manager: Arc<RevisionManager>,
@@ -32,10 +32,10 @@ pub struct ClientBlockEditor {
     edit_cmd_tx: EditorCommandSender,
 }
 
-impl ClientBlockEditor {
+impl ClientTextBlockEditor {
     pub(crate) async fn new(
         doc_id: &str,
-        user: Arc<dyn BlockUser>,
+        user: Arc<dyn TextBlockUser>,
         mut rev_manager: RevisionManager,
         rev_web_socket: Arc<dyn RevisionWebSocket>,
         cloud_service: Arc<dyn RevisionCloudService>,
@@ -174,7 +174,7 @@ impl ClientBlockEditor {
     }
 }
 
-impl std::ops::Drop for ClientBlockEditor {
+impl std::ops::Drop for ClientTextBlockEditor {
     fn drop(&mut self) {
         tracing::trace!("{} ClientBlockEditor was dropped", self.doc_id)
     }
@@ -182,7 +182,7 @@ impl std::ops::Drop for ClientBlockEditor {
 
 // The edit queue will exit after the EditorCommandSender was dropped.
 fn spawn_edit_queue(
-    user: Arc<dyn BlockUser>,
+    user: Arc<dyn TextBlockUser>,
     rev_manager: Arc<RevisionManager>,
     delta: RichTextDelta,
 ) -> EditorCommandSender {
@@ -193,7 +193,7 @@ fn spawn_edit_queue(
 }
 
 #[cfg(feature = "flowy_unit_test")]
-impl ClientBlockEditor {
+impl ClientTextBlockEditor {
     pub async fn doc_json(&self) -> FlowyResult<String> {
         let (ret, rx) = oneshot::channel::<CollaborateResult<String>>();
         let msg = EditorCommand::ReadDeltaStr { ret };
@@ -217,14 +217,14 @@ impl ClientBlockEditor {
 
 struct BlockInfoBuilder();
 impl RevisionObjectBuilder for BlockInfoBuilder {
-    type Output = BlockInfo;
+    type Output = TextBlockInfo;
 
     fn build_object(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
         let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
         let mut delta = make_delta_from_revisions(revisions)?;
         correct_delta(&mut delta);
 
-        Result::<BlockInfo, FlowyError>::Ok(BlockInfo {
+        Result::<TextBlockInfo, FlowyError>::Ok(TextBlockInfo {
             block_id: object_id.to_owned(),
             text: delta.to_delta_str(),
             rev_id,

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

@@ -1,28 +1,28 @@
 use crate::entities::{ExportData, ExportParams, ExportPayload};
-use crate::BlockManager;
-use flowy_collaboration::entities::document_info::{BlockDelta, BlockId};
+use crate::TextBlockManager;
+use flowy_collaboration::entities::text_block_info::{TextBlockDelta, TextBlockId};
 use flowy_error::FlowyError;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::convert::TryInto;
 use std::sync::Arc;
 
 pub(crate) async fn get_block_data_handler(
-    data: Data<BlockId>,
-    manager: AppData<Arc<BlockManager>>,
-) -> DataResult<BlockDelta, FlowyError> {
-    let block_id: BlockId = data.into_inner();
+    data: Data<TextBlockId>,
+    manager: AppData<Arc<TextBlockManager>>,
+) -> DataResult<TextBlockDelta, FlowyError> {
+    let block_id: TextBlockId = data.into_inner();
     let editor = manager.open_block(&block_id).await?;
     let delta_str = editor.delta_str().await?;
-    data_result(BlockDelta {
+    data_result(TextBlockDelta {
         block_id: block_id.into(),
         delta_str,
     })
 }
 
 pub(crate) async fn apply_delta_handler(
-    data: Data<BlockDelta>,
-    manager: AppData<Arc<BlockManager>>,
-) -> DataResult<BlockDelta, FlowyError> {
+    data: Data<TextBlockDelta>,
+    manager: AppData<Arc<TextBlockManager>>,
+) -> DataResult<TextBlockDelta, FlowyError> {
     let block_delta = manager.receive_local_delta(data.into_inner()).await?;
     data_result(block_delta)
 }
@@ -30,7 +30,7 @@ pub(crate) async fn apply_delta_handler(
 #[tracing::instrument(skip(data, manager), err)]
 pub(crate) async fn export_handler(
     data: Data<ExportPayload>,
-    manager: AppData<Arc<BlockManager>>,
+    manager: AppData<Arc<TextBlockManager>>,
 ) -> DataResult<ExportData, FlowyError> {
     let params: ExportParams = data.into_inner().try_into()?;
     let editor = manager.open_block(&params.view_id).await?;

+ 2 - 2
frontend/rust-lib/flowy-block/src/event_map.rs

@@ -1,11 +1,11 @@
 use crate::event_handler::*;
-use crate::BlockManager;
+use crate::TextBlockManager;
 use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
 use lib_dispatch::prelude::Module;
 use std::sync::Arc;
 use strum_macros::Display;
 
-pub fn create(block_manager: Arc<BlockManager>) -> Module {
+pub fn create(block_manager: Arc<TextBlockManager>) -> Module {
     let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(block_manager);
 
     module = module

+ 7 - 5
frontend/rust-lib/flowy-block/src/lib.rs

@@ -1,4 +1,4 @@
-pub mod block_editor;
+pub mod editor;
 mod entities;
 mod event_handler;
 pub mod event_map;
@@ -15,13 +15,15 @@ pub mod errors {
 pub const DOCUMENT_SYNC_INTERVAL_IN_MILLIS: u64 = 1000;
 
 use crate::errors::FlowyError;
-use flowy_collaboration::entities::document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams};
+use flowy_collaboration::entities::text_block_info::{
+    CreateTextBlockParams, ResetTextBlockParams, TextBlockId, TextBlockInfo,
+};
 use lib_infra::future::FutureResult;
 
 pub trait BlockCloudService: Send + Sync {
-    fn create_block(&self, token: &str, params: CreateBlockParams) -> FutureResult<(), FlowyError>;
+    fn create_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError>;
 
-    fn read_block(&self, token: &str, params: BlockId) -> FutureResult<Option<BlockInfo>, FlowyError>;
+    fn read_block(&self, token: &str, params: TextBlockId) -> FutureResult<Option<TextBlockInfo>, FlowyError>;
 
-    fn update_block(&self, token: &str, params: ResetBlockParams) -> FutureResult<(), FlowyError>;
+    fn update_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError>;
 }

+ 40 - 49
frontend/rust-lib/flowy-block/src/manager.rs

@@ -1,9 +1,9 @@
-use crate::{block_editor::ClientBlockEditor, errors::FlowyError, BlockCloudService};
+use crate::{editor::ClientTextBlockEditor, errors::FlowyError, BlockCloudService};
 use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_collaboration::entities::{
-    document_info::{BlockDelta, BlockId},
     revision::{md5, RepeatedRevision, Revision},
+    text_block_info::{TextBlockDelta, TextBlockId},
     ws_data::ServerRevisionWSData,
 };
 use flowy_database::ConnectionPool;
@@ -12,43 +12,42 @@ use flowy_sync::{RevisionCloudService, RevisionManager, RevisionPersistence, Rev
 use lib_infra::future::FutureResult;
 use std::{convert::TryInto, sync::Arc};
 
-pub trait BlockUser: Send + Sync {
+pub trait TextBlockUser: Send + Sync {
     fn user_dir(&self) -> Result<String, FlowyError>;
     fn user_id(&self) -> Result<String, FlowyError>;
     fn token(&self) -> Result<String, FlowyError>;
     fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError>;
 }
 
-pub struct BlockManager {
+pub struct TextBlockManager {
     cloud_service: Arc<dyn BlockCloudService>,
     rev_web_socket: Arc<dyn RevisionWebSocket>,
-    block_editors: Arc<BlockEditors>,
-    block_user: Arc<dyn BlockUser>,
+    editor_map: Arc<TextBlockEditorMap>,
+    user: Arc<dyn TextBlockUser>,
 }
 
-impl BlockManager {
+impl TextBlockManager {
     pub fn new(
         cloud_service: Arc<dyn BlockCloudService>,
-        block_user: Arc<dyn BlockUser>,
+        text_block_user: Arc<dyn TextBlockUser>,
         rev_web_socket: Arc<dyn RevisionWebSocket>,
     ) -> Self {
-        let block_editors = Arc::new(BlockEditors::new());
         Self {
             cloud_service,
             rev_web_socket,
-            block_editors,
-            block_user,
+            editor_map: Arc::new(TextBlockEditorMap::new()),
+            user: text_block_user,
         }
     }
 
     pub fn init(&self) -> FlowyResult<()> {
-        listen_ws_state_changed(self.rev_web_socket.clone(), self.block_editors.clone());
+        listen_ws_state_changed(self.rev_web_socket.clone(), self.editor_map.clone());
 
         Ok(())
     }
 
     #[tracing::instrument(level = "debug", skip(self, block_id), fields(block_id), err)]
-    pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<ClientBlockEditor>, FlowyError> {
+    pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
         let block_id = block_id.as_ref();
         tracing::Span::current().record("block_id", &block_id);
         self.get_block_editor(block_id).await
@@ -58,7 +57,7 @@ impl BlockManager {
     pub fn close_block<T: AsRef<str>>(&self, block_id: T) -> Result<(), FlowyError> {
         let block_id = block_id.as_ref();
         tracing::Span::current().record("block_id", &block_id);
-        self.block_editors.remove(block_id);
+        self.editor_map.remove(block_id);
         Ok(())
     }
 
@@ -66,16 +65,16 @@ impl BlockManager {
     pub fn delete_block<T: AsRef<str>>(&self, doc_id: T) -> Result<(), FlowyError> {
         let doc_id = doc_id.as_ref();
         tracing::Span::current().record("doc_id", &doc_id);
-        self.block_editors.remove(doc_id);
+        self.editor_map.remove(doc_id);
         Ok(())
     }
 
     #[tracing::instrument(level = "debug", skip(self, delta), fields(doc_id = %delta.block_id), err)]
-    pub async fn receive_local_delta(&self, delta: BlockDelta) -> Result<BlockDelta, FlowyError> {
+    pub async fn receive_local_delta(&self, delta: TextBlockDelta) -> Result<TextBlockDelta, FlowyError> {
         let editor = self.get_block_editor(&delta.block_id).await?;
         let _ = editor.compose_local_delta(Bytes::from(delta.delta_str)).await?;
         let document_json = editor.delta_str().await?;
-        Ok(BlockDelta {
+        Ok(TextBlockDelta {
             block_id: delta.block_id.clone(),
             delta_str: document_json,
         })
@@ -83,7 +82,7 @@ impl BlockManager {
 
     pub async fn create_block<T: AsRef<str>>(&self, doc_id: T, revisions: RepeatedRevision) -> FlowyResult<()> {
         let doc_id = doc_id.as_ref().to_owned();
-        let db_pool = self.block_user.db_pool()?;
+        let db_pool = self.user.db_pool()?;
         // Maybe we could save the block to disk without creating the RevisionManager
         let rev_manager = self.make_block_rev_manager(&doc_id, db_pool)?;
         let _ = rev_manager.reset_object(revisions).await?;
@@ -93,9 +92,9 @@ impl BlockManager {
     pub async fn receive_ws_data(&self, data: Bytes) {
         let result: Result<ServerRevisionWSData, protobuf::ProtobufError> = data.try_into();
         match result {
-            Ok(data) => match self.block_editors.get(&data.object_id) {
+            Ok(data) => match self.editor_map.get(&data.object_id) {
                 None => tracing::error!("Can't find any source handler for {:?}-{:?}", data.object_id, data.ty),
-                Some(block_editor) => match block_editor.receive_ws_data(data).await {
+                Some(editor) => match editor.receive_ws_data(data).await {
                     Ok(_) => {}
                     Err(e) => tracing::error!("{}", e),
                 },
@@ -107,11 +106,11 @@ impl BlockManager {
     }
 }
 
-impl BlockManager {
-    async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientBlockEditor>> {
-        match self.block_editors.get(block_id) {
+impl TextBlockManager {
+    async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientTextBlockEditor>> {
+        match self.editor_map.get(block_id) {
             None => {
-                let db_pool = self.block_user.db_pool()?;
+                let db_pool = self.user.db_pool()?;
                 self.make_block_editor(block_id, db_pool).await
             }
             Some(editor) => Ok(editor),
@@ -122,36 +121,36 @@ impl BlockManager {
         &self,
         block_id: &str,
         pool: Arc<ConnectionPool>,
-    ) -> Result<Arc<ClientBlockEditor>, FlowyError> {
-        let user = self.block_user.clone();
-        let token = self.block_user.token()?;
+    ) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
+        let user = self.user.clone();
+        let token = self.user.token()?;
         let rev_manager = self.make_block_rev_manager(block_id, pool.clone())?;
-        let cloud_service = Arc::new(BlockRevisionCloudService {
+        let cloud_service = Arc::new(TextBlockRevisionCloudService {
             token,
             server: self.cloud_service.clone(),
         });
         let doc_editor =
-            ClientBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
-        self.block_editors.insert(block_id, &doc_editor);
+            ClientTextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
+        self.editor_map.insert(block_id, &doc_editor);
         Ok(doc_editor)
     }
 
     fn make_block_rev_manager(&self, doc_id: &str, pool: Arc<ConnectionPool>) -> Result<RevisionManager, FlowyError> {
-        let user_id = self.block_user.user_id()?;
+        let user_id = self.user.user_id()?;
         let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, doc_id, pool));
         Ok(RevisionManager::new(&user_id, doc_id, rev_persistence))
     }
 }
 
-struct BlockRevisionCloudService {
+struct TextBlockRevisionCloudService {
     token: String,
     server: Arc<dyn BlockCloudService>,
 }
 
-impl RevisionCloudService for BlockRevisionCloudService {
+impl RevisionCloudService for TextBlockRevisionCloudService {
     #[tracing::instrument(level = "trace", skip(self))]
     fn fetch_object(&self, user_id: &str, object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
-        let params: BlockId = object_id.to_string().into();
+        let params: TextBlockId = object_id.to_string().into();
         let server = self.server.clone();
         let token = self.token.clone();
         let user_id = user_id.to_string();
@@ -177,32 +176,24 @@ impl RevisionCloudService for BlockRevisionCloudService {
     }
 }
 
-pub struct BlockEditors {
-    inner: DashMap<String, Arc<ClientBlockEditor>>,
+pub struct TextBlockEditorMap {
+    inner: DashMap<String, Arc<ClientTextBlockEditor>>,
 }
 
-impl BlockEditors {
+impl TextBlockEditorMap {
     fn new() -> Self {
         Self { inner: DashMap::new() }
     }
 
-    pub(crate) fn insert(&self, block_id: &str, doc: &Arc<ClientBlockEditor>) {
+    pub(crate) fn insert(&self, block_id: &str, doc: &Arc<ClientTextBlockEditor>) {
         if self.inner.contains_key(block_id) {
             log::warn!("Doc:{} already exists in cache", block_id);
         }
         self.inner.insert(block_id.to_string(), doc.clone());
     }
 
-    pub(crate) fn contains(&self, block_id: &str) -> bool {
-        self.inner.get(block_id).is_some()
-    }
-
-    pub(crate) fn get(&self, block_id: &str) -> Option<Arc<ClientBlockEditor>> {
-        if !self.contains(block_id) {
-            return None;
-        }
-        let opened_doc = self.inner.get(block_id).unwrap();
-        Some(opened_doc.clone())
+    pub(crate) fn get(&self, block_id: &str) -> Option<Arc<ClientTextBlockEditor>> {
+        Some(self.inner.get(block_id)?.clone())
     }
 
     pub(crate) fn remove(&self, block_id: &str) {
@@ -214,7 +205,7 @@ impl BlockEditors {
 }
 
 #[tracing::instrument(level = "trace", skip(web_socket, handlers))]
-fn listen_ws_state_changed(web_socket: Arc<dyn RevisionWebSocket>, handlers: Arc<BlockEditors>) {
+fn listen_ws_state_changed(web_socket: Arc<dyn RevisionWebSocket>, handlers: Arc<TextBlockEditorMap>) {
     tokio::spawn(async move {
         let mut notify = web_socket.subscribe_state_changed().await;
         while let Ok(state) = notify.recv().await {

+ 3 - 3
frontend/rust-lib/flowy-block/src/queue.rs

@@ -1,5 +1,5 @@
 use crate::web_socket::EditorCommandReceiver;
-use crate::BlockUser;
+use crate::TextBlockUser;
 use async_stream::stream;
 use flowy_collaboration::util::make_delta_from_revisions;
 use flowy_collaboration::{
@@ -21,14 +21,14 @@ use tokio::sync::{oneshot, RwLock};
 // serial.
 pub(crate) struct EditBlockQueue {
     document: Arc<RwLock<ClientDocument>>,
-    user: Arc<dyn BlockUser>,
+    user: Arc<dyn TextBlockUser>,
     rev_manager: Arc<RevisionManager>,
     receiver: Option<EditorCommandReceiver>,
 }
 
 impl EditBlockQueue {
     pub(crate) fn new(
-        user: Arc<dyn BlockUser>,
+        user: Arc<dyn TextBlockUser>,
         rev_manager: Arc<RevisionManager>,
         delta: RichTextDelta,
         receiver: EditorCommandReceiver,

+ 10 - 10
frontend/rust-lib/flowy-block/src/web_socket.rs

@@ -31,11 +31,11 @@ pub(crate) async fn make_block_ws_manager(
     rev_web_socket: Arc<dyn RevisionWebSocket>,
 ) -> Arc<RevisionWebSocketManager> {
     let ws_data_provider = Arc::new(WSDataProvider::new(&doc_id, Arc::new(rev_manager.clone())));
-    let resolver = Arc::new(BlockConflictResolver { edit_cmd_tx });
+    let resolver = Arc::new(TextBlockConflictResolver { edit_cmd_tx });
     let conflict_controller =
         RichTextConflictController::new(&user_id, resolver, Arc::new(ws_data_provider.clone()), rev_manager);
-    let ws_data_stream = Arc::new(BlockRevisionWSDataStream::new(conflict_controller));
-    let ws_data_sink = Arc::new(BlockWSDataSink(ws_data_provider));
+    let ws_data_stream = Arc::new(TextBlockRevisionWSDataStream::new(conflict_controller));
+    let ws_data_sink = Arc::new(TextBlockWSDataSink(ws_data_provider));
     let ping_duration = Duration::from_millis(DOCUMENT_SYNC_INTERVAL_IN_MILLIS);
     let ws_manager = Arc::new(RevisionWebSocketManager::new(
         "Block",
@@ -62,11 +62,11 @@ fn listen_document_ws_state(_user_id: &str, _doc_id: &str, mut subscriber: broad
     });
 }
 
-pub(crate) struct BlockRevisionWSDataStream {
+pub(crate) struct TextBlockRevisionWSDataStream {
     conflict_controller: Arc<RichTextConflictController>,
 }
 
-impl BlockRevisionWSDataStream {
+impl TextBlockRevisionWSDataStream {
     pub fn new(conflict_controller: RichTextConflictController) -> Self {
         Self {
             conflict_controller: Arc::new(conflict_controller),
@@ -74,7 +74,7 @@ impl BlockRevisionWSDataStream {
     }
 }
 
-impl RevisionWSDataStream for BlockRevisionWSDataStream {
+impl RevisionWSDataStream for TextBlockRevisionWSDataStream {
     fn receive_push_revision(&self, bytes: Bytes) -> BoxResultFuture<(), FlowyError> {
         let resolver = self.conflict_controller.clone();
         Box::pin(async move { resolver.receive_bytes(bytes).await })
@@ -96,19 +96,19 @@ impl RevisionWSDataStream for BlockRevisionWSDataStream {
     }
 }
 
-pub(crate) struct BlockWSDataSink(pub(crate) Arc<WSDataProvider>);
-impl RevisionWebSocketSink for BlockWSDataSink {
+pub(crate) struct TextBlockWSDataSink(pub(crate) Arc<WSDataProvider>);
+impl RevisionWebSocketSink for TextBlockWSDataSink {
     fn next(&self) -> FutureResult<Option<ClientRevisionWSData>, FlowyError> {
         let sink_provider = self.0.clone();
         FutureResult::new(async move { sink_provider.next().await })
     }
 }
 
-struct BlockConflictResolver {
+struct TextBlockConflictResolver {
     edit_cmd_tx: EditorCommandSender,
 }
 
-impl ConflictResolver<RichTextAttributes> for BlockConflictResolver {
+impl ConflictResolver<RichTextAttributes> for TextBlockConflictResolver {
     fn compose_delta(&self, delta: RichTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
         let tx = self.edit_cmd_tx.clone();
         Box::pin(async move {

+ 3 - 3
frontend/rust-lib/flowy-block/tests/document/edit_script.rs

@@ -1,4 +1,4 @@
-use flowy_block::block_editor::ClientBlockEditor;
+use flowy_block::editor::ClientTextBlockEditor;
 use flowy_block::DOCUMENT_SYNC_INTERVAL_IN_MILLIS;
 use flowy_collaboration::entities::revision::RevisionState;
 use flowy_test::{helper::ViewTest, FlowySDKTest};
@@ -19,7 +19,7 @@ pub enum EditorScript {
 
 pub struct EditorTest {
     pub sdk: FlowySDKTest,
-    pub editor: Arc<ClientBlockEditor>,
+    pub editor: Arc<ClientTextBlockEditor>,
 }
 
 impl EditorTest {
@@ -27,7 +27,7 @@ impl EditorTest {
         let sdk = FlowySDKTest::default();
         let _ = sdk.init_user().await;
         let test = ViewTest::new(&sdk).await;
-        let editor = sdk.block_manager.open_block(&test.view.id).await.unwrap();
+        let editor = sdk.text_block_manager.open_block(&test.view.id).await.unwrap();
         Self { sdk, editor }
     }
 

+ 2 - 1
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql

@@ -1,2 +1,3 @@
 -- This file should undo anything in `up.sql`
-DROP TABLE kv_table;
+DROP TABLE kv_table;
+DROP TABLE grid_rev_table;

+ 9 - 0
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql

@@ -2,4 +2,13 @@
 CREATE TABLE kv_table (
    key TEXT NOT NULL PRIMARY KEY,
    value BLOB NOT NULL DEFAULT (x'')
+);
+
+CREATE TABLE grid_rev_table (
+   id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+   object_id TEXT NOT NULL DEFAULT '',
+   base_rev_id BIGINT NOT NULL DEFAULT 0,
+   rev_id BIGINT NOT NULL DEFAULT 0,
+   data BLOB NOT NULL DEFAULT (x''),
+   state INTEGER NOT NULL DEFAULT 0
 );

+ 2 - 2
frontend/rust-lib/flowy-folder/src/services/view/controller.rs

@@ -14,8 +14,8 @@ use crate::{
 };
 use bytes::Bytes;
 use flowy_collaboration::entities::{
-    document_info::BlockId,
     revision::{RepeatedRevision, Revision},
+    text_block_info::TextBlockId,
 };
 use flowy_database::kv::KV;
 use flowy_folder_data_model::entities::view::ViewDataType;
@@ -147,7 +147,7 @@ impl ViewController {
     }
 
     #[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)]
-    pub(crate) async fn delete_view(&self, params: BlockId) -> Result<(), FlowyError> {
+    pub(crate) async fn delete_view(&self, params: TextBlockId) -> Result<(), FlowyError> {
         if let Some(view_id) = KV::get_str(LATEST_VIEW_ID) {
             if view_id == params.value {
                 let _ = KV::remove(LATEST_VIEW_ID);

+ 3 - 3
frontend/rust-lib/flowy-folder/tests/workspace/helper.rs

@@ -1,4 +1,4 @@
-use flowy_collaboration::entities::document_info::BlockInfo;
+use flowy_collaboration::entities::text_block_info::TextBlockInfo;
 use flowy_folder::event_map::FolderEvent::*;
 use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId};
 use flowy_folder_data_model::entities::workspace::WorkspaceId;
@@ -161,14 +161,14 @@ pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec<String>) {
         .await;
 }
 
-pub async fn open_document(sdk: &FlowySDKTest, view_id: &str) -> BlockInfo {
+pub async fn open_document(sdk: &FlowySDKTest, view_id: &str) -> TextBlockInfo {
     let view_id: ViewId = view_id.into();
     FolderEventBuilder::new(sdk.clone())
         .event(SetLatestView)
         .payload(view_id)
         .async_send()
         .await
-        .parse::<BlockInfo>()
+        .parse::<TextBlockInfo>()
 }
 
 pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash {

+ 2 - 2
frontend/rust-lib/flowy-folder/tests/workspace/script.rs

@@ -1,5 +1,5 @@
 use crate::helper::*;
-use flowy_collaboration::entities::{document_info::BlockInfo, revision::RevisionState};
+use flowy_collaboration::entities::{revision::RevisionState, text_block_info::TextBlockInfo};
 use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor};
 use flowy_folder_data_model::entities::{
     app::{App, RepeatedApp},
@@ -58,7 +58,7 @@ pub struct FolderTest {
     pub app: App,
     pub view: View,
     pub trash: Vec<Trash>,
-    pub document_info: Option<BlockInfo>,
+    pub document_info: Option<TextBlockInfo>,
     // pub folder_editor:
 }
 

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

@@ -44,7 +44,7 @@ impl GridBuilder {
         let grid_meta = GridMeta {
             grid_id: self.grid_id,
             fields: self.fields,
-            rows: self.rows,
+            blocks: vec![],
         };
 
         // let _ = check_rows(&self.fields, &self.rows)?;

+ 13 - 7
frontend/rust-lib/flowy-net/src/http_server/document.rs

@@ -3,7 +3,9 @@ use crate::{
     request::{HttpRequestBuilder, ResponseMiddleware},
 };
 use flowy_block::BlockCloudService;
-use flowy_collaboration::entities::document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams};
+use flowy_collaboration::entities::text_block_info::{
+    CreateTextBlockParams, ResetTextBlockParams, TextBlockId, TextBlockInfo,
+};
 use flowy_error::FlowyError;
 use http_flowy::response::FlowyResponse;
 use lazy_static::lazy_static;
@@ -21,26 +23,26 @@ impl BlockHttpCloudService {
 }
 
 impl BlockCloudService for BlockHttpCloudService {
-    fn create_block(&self, token: &str, params: CreateBlockParams) -> FutureResult<(), FlowyError> {
+    fn create_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
         FutureResult::new(async move { create_document_request(&token, params, &url).await })
     }
 
-    fn read_block(&self, token: &str, params: BlockId) -> FutureResult<Option<BlockInfo>, FlowyError> {
+    fn read_block(&self, token: &str, params: TextBlockId) -> FutureResult<Option<TextBlockInfo>, FlowyError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
         FutureResult::new(async move { read_document_request(&token, params, &url).await })
     }
 
-    fn update_block(&self, token: &str, params: ResetBlockParams) -> FutureResult<(), FlowyError> {
+    fn update_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
         FutureResult::new(async move { reset_doc_request(&token, params, &url).await })
     }
 }
 
-pub async fn create_document_request(token: &str, params: CreateBlockParams, url: &str) -> Result<(), FlowyError> {
+pub async fn create_document_request(token: &str, params: CreateTextBlockParams, url: &str) -> Result<(), FlowyError> {
     let _ = request_builder()
         .post(&url.to_owned())
         .header(HEADER_TOKEN, token)
@@ -50,7 +52,11 @@ pub async fn create_document_request(token: &str, params: CreateBlockParams, url
     Ok(())
 }
 
-pub async fn read_document_request(token: &str, params: BlockId, url: &str) -> Result<Option<BlockInfo>, FlowyError> {
+pub async fn read_document_request(
+    token: &str,
+    params: TextBlockId,
+    url: &str,
+) -> Result<Option<TextBlockInfo>, FlowyError> {
     let doc = request_builder()
         .get(&url.to_owned())
         .header(HEADER_TOKEN, token)
@@ -61,7 +67,7 @@ pub async fn read_document_request(token: &str, params: BlockId, url: &str) -> R
     Ok(doc)
 }
 
-pub async fn reset_doc_request(token: &str, params: ResetBlockParams, url: &str) -> Result<(), FlowyError> {
+pub async fn reset_doc_request(token: &str, params: ResetTextBlockParams, url: &str) -> Result<(), FlowyError> {
     let _ = request_builder()
         .patch(&url.to_owned())
         .header(HEADER_TOKEN, token)

+ 3 - 3
frontend/rust-lib/flowy-net/src/local_server/persistence.rs

@@ -1,5 +1,5 @@
 use flowy_collaboration::{
-    entities::{document_info::BlockInfo, folder_info::FolderInfo},
+    entities::{folder_info::FolderInfo, text_block_info::TextBlockInfo},
     errors::CollaborateError,
     protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB},
     server_document::*,
@@ -111,7 +111,7 @@ impl FolderCloudPersistence for LocalDocumentCloudPersistence {
 }
 
 impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
-    fn read_document(&self, doc_id: &str) -> BoxResultFuture<BlockInfo, CollaborateError> {
+    fn read_document(&self, doc_id: &str) -> BoxResultFuture<TextBlockInfo, CollaborateError> {
         let storage = self.storage.clone();
         let doc_id = doc_id.to_owned();
         Box::pin(async move {
@@ -127,7 +127,7 @@ impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
         &self,
         doc_id: &str,
         repeated_revision: RepeatedRevisionPB,
-    ) -> BoxResultFuture<Option<BlockInfo>, CollaborateError> {
+    ) -> BoxResultFuture<Option<TextBlockInfo>, CollaborateError> {
         let doc_id = doc_id.to_owned();
         let storage = self.storage.clone();
         Box::pin(async move {

+ 5 - 5
frontend/rust-lib/flowy-net/src/local_server/server.rs

@@ -4,7 +4,7 @@ use bytes::Bytes;
 use flowy_collaboration::{
     client_document::default::initial_quill_delta_string,
     entities::{
-        document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams},
+        text_block_info::{CreateTextBlockParams, ResetTextBlockParams, TextBlockId, TextBlockInfo},
         ws_data::{ClientRevisionWSData, ClientRevisionWSDataType},
     },
     errors::CollaborateError,
@@ -413,12 +413,12 @@ impl UserCloudService for LocalServer {
 }
 
 impl BlockCloudService for LocalServer {
-    fn create_block(&self, _token: &str, _params: CreateBlockParams) -> FutureResult<(), FlowyError> {
+    fn create_block(&self, _token: &str, _params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
         FutureResult::new(async { Ok(()) })
     }
 
-    fn read_block(&self, _token: &str, params: BlockId) -> FutureResult<Option<BlockInfo>, FlowyError> {
-        let doc = BlockInfo {
+    fn read_block(&self, _token: &str, params: TextBlockId) -> FutureResult<Option<TextBlockInfo>, FlowyError> {
+        let doc = TextBlockInfo {
             block_id: params.value,
             text: initial_quill_delta_string(),
             rev_id: 0,
@@ -427,7 +427,7 @@ impl BlockCloudService for LocalServer {
         FutureResult::new(async { Ok(Some(doc)) })
     }
 
-    fn update_block(&self, _token: &str, _params: ResetBlockParams) -> FutureResult<(), FlowyError> {
+    fn update_block(&self, _token: &str, _params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
         FutureResult::new(async { Ok(()) })
     }
 }

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

@@ -1,5 +1,5 @@
 use bytes::Bytes;
-use flowy_block::BlockManager;
+use flowy_block::TextBlockManager;
 use flowy_collaboration::client_document::default::initial_quill_delta_string;
 use flowy_collaboration::entities::revision::RepeatedRevision;
 use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
@@ -32,7 +32,7 @@ impl FolderDepsResolver {
         user_session: Arc<UserSession>,
         server_config: &ClientServerConfiguration,
         ws_conn: &Arc<FlowyWebSocketConnect>,
-        block_manager: &Arc<BlockManager>,
+        text_block_manager: &Arc<TextBlockManager>,
         grid_manager: &Arc<GridManager>,
     ) -> Arc<FolderManager> {
         let user: Arc<dyn WorkspaceUser> = Arc::new(WorkspaceUserImpl(user_session.clone()));
@@ -43,7 +43,7 @@ impl FolderDepsResolver {
             Some(local_server) => local_server,
         };
 
-        let view_data_processor = make_view_data_processor(block_manager.clone(), grid_manager.clone());
+        let view_data_processor = make_view_data_processor(text_block_manager.clone(), grid_manager.clone());
         let folder_manager =
             Arc::new(FolderManager::new(user.clone(), cloud_service, database, view_data_processor, web_socket).await);
 
@@ -60,10 +60,13 @@ impl FolderDepsResolver {
     }
 }
 
-fn make_view_data_processor(block_manager: Arc<BlockManager>, grid_manager: Arc<GridManager>) -> ViewDataProcessorMap {
+fn make_view_data_processor(
+    text_block_manager: Arc<TextBlockManager>,
+    grid_manager: Arc<GridManager>,
+) -> ViewDataProcessorMap {
     let mut map: HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
 
-    let block_data_impl = BlockManagerViewDataImpl(block_manager);
+    let block_data_impl = BlockManagerViewDataImpl(text_block_manager);
     map.insert(block_data_impl.data_type(), Arc::new(block_data_impl));
 
     let grid_data_impl = GridManagerViewDataImpl(grid_manager);
@@ -130,45 +133,45 @@ impl WSMessageReceiver for FolderWSMessageReceiverImpl {
     }
 }
 
-struct BlockManagerViewDataImpl(Arc<BlockManager>);
+struct BlockManagerViewDataImpl(Arc<TextBlockManager>);
 impl ViewDataProcessor for BlockManagerViewDataImpl {
     fn initialize(&self) -> FutureResult<(), FlowyError> {
-        let block_manager = self.0.clone();
-        FutureResult::new(async move { block_manager.init() })
+        let manager = self.0.clone();
+        FutureResult::new(async move { manager.init() })
     }
 
     fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FutureResult<(), FlowyError> {
-        let block_manager = self.0.clone();
+        let manager = self.0.clone();
         let view_id = view_id.to_string();
         FutureResult::new(async move {
-            let _ = block_manager.create_block(view_id, repeated_revision).await?;
+            let _ = manager.create_block(view_id, repeated_revision).await?;
             Ok(())
         })
     }
 
     fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
-        let block_manager = self.0.clone();
+        let manager = self.0.clone();
         let view_id = view_id.to_string();
         FutureResult::new(async move {
-            let _ = block_manager.delete_block(view_id)?;
+            let _ = manager.delete_block(view_id)?;
             Ok(())
         })
     }
 
     fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
-        let block_manager = self.0.clone();
+        let manager = self.0.clone();
         let view_id = view_id.to_string();
         FutureResult::new(async move {
-            let _ = block_manager.close_block(view_id)?;
+            let _ = manager.close_block(view_id)?;
             Ok(())
         })
     }
 
     fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError> {
         let view_id = view_id.to_string();
-        let block_manager = self.0.clone();
+        let manager = self.0.clone();
         FutureResult::new(async move {
-            let editor = block_manager.open_block(view_id).await?;
+            let editor = manager.open_block(view_id).await?;
             let delta_str = editor.delta_str().await?;
             Ok(delta_str)
         })

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

@@ -1,10 +1,10 @@
-mod block_deps;
 mod folder_deps;
 mod grid_deps;
+mod text_block_deps;
 mod user_deps;
 mod util;
 
-pub use block_deps::*;
 pub use folder_deps::*;
 pub use grid_deps::*;
+pub use text_block_deps::*;
 pub use user_deps::*;

+ 10 - 10
frontend/rust-lib/flowy-sdk/src/deps_resolve/block_deps.rs → frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs

@@ -1,7 +1,7 @@
 use bytes::Bytes;
 use flowy_block::{
     errors::{internal_error, FlowyError},
-    BlockCloudService, BlockManager, BlockUser,
+    BlockCloudService, TextBlockManager, TextBlockUser,
 };
 use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
 use flowy_database::ConnectionPool;
@@ -16,22 +16,22 @@ use lib_infra::future::BoxResultFuture;
 use lib_ws::{WSChannel, WSMessageReceiver, WebSocketRawMessage};
 use std::{convert::TryInto, path::Path, sync::Arc};
 
-pub struct BlockDepsResolver();
-impl BlockDepsResolver {
+pub struct TextBlockDepsResolver();
+impl TextBlockDepsResolver {
     pub fn resolve(
         local_server: Option<Arc<LocalServer>>,
         ws_conn: Arc<FlowyWebSocketConnect>,
         user_session: Arc<UserSession>,
         server_config: &ClientServerConfiguration,
-    ) -> Arc<BlockManager> {
+    ) -> Arc<TextBlockManager> {
         let user = Arc::new(BlockUserImpl(user_session));
-        let rev_web_socket = Arc::new(BlockWebSocket(ws_conn.clone()));
+        let rev_web_socket = Arc::new(TextBlockWebSocket(ws_conn.clone()));
         let cloud_service: Arc<dyn BlockCloudService> = match local_server {
             None => Arc::new(BlockHttpCloudService::new(server_config.clone())),
             Some(local_server) => local_server,
         };
 
-        let manager = Arc::new(BlockManager::new(cloud_service, user, rev_web_socket));
+        let manager = Arc::new(TextBlockManager::new(cloud_service, user, rev_web_socket));
         let receiver = Arc::new(DocumentWSMessageReceiverImpl(manager.clone()));
         ws_conn.add_ws_message_receiver(receiver).unwrap();
 
@@ -40,7 +40,7 @@ impl BlockDepsResolver {
 }
 
 struct BlockUserImpl(Arc<UserSession>);
-impl BlockUser for BlockUserImpl {
+impl TextBlockUser for BlockUserImpl {
     fn user_dir(&self) -> Result<String, FlowyError> {
         let dir = self.0.user_dir().map_err(|e| FlowyError::unauthorized().context(e))?;
 
@@ -64,8 +64,8 @@ impl BlockUser for BlockUserImpl {
     }
 }
 
-struct BlockWebSocket(Arc<FlowyWebSocketConnect>);
-impl RevisionWebSocket for BlockWebSocket {
+struct TextBlockWebSocket(Arc<FlowyWebSocketConnect>);
+impl RevisionWebSocket for TextBlockWebSocket {
     fn send(&self, data: ClientRevisionWSData) -> BoxResultFuture<(), FlowyError> {
         let bytes: Bytes = data.try_into().unwrap();
         let msg = WebSocketRawMessage {
@@ -90,7 +90,7 @@ impl RevisionWebSocket for BlockWebSocket {
     }
 }
 
-struct DocumentWSMessageReceiverImpl(Arc<BlockManager>);
+struct DocumentWSMessageReceiverImpl(Arc<TextBlockManager>);
 impl WSMessageReceiver for DocumentWSMessageReceiverImpl {
     fn source(&self) -> WSChannel {
         WSChannel::Document

+ 20 - 8
frontend/rust-lib/flowy-sdk/src/lib.rs

@@ -3,7 +3,7 @@ pub mod module;
 pub use flowy_net::get_client_server_configuration;
 
 use crate::deps_resolve::*;
-use flowy_block::BlockManager;
+use flowy_block::TextBlockManager;
 use flowy_folder::{errors::FlowyError, manager::FolderManager};
 use flowy_grid::manager::GridManager;
 use flowy_net::ClientServerConfiguration;
@@ -88,7 +88,7 @@ pub struct FlowySDK {
     #[allow(dead_code)]
     config: FlowySDKConfig,
     pub user_session: Arc<UserSession>,
-    pub block_manager: Arc<BlockManager>,
+    pub text_block_manager: Arc<TextBlockManager>,
     pub folder_manager: Arc<FolderManager>,
     pub grid_manager: Arc<GridManager>,
     pub dispatcher: Arc<EventDispatcher>,
@@ -103,9 +103,9 @@ impl FlowySDK {
         tracing::debug!("🔥 {:?}", config);
         let runtime = tokio_default_runtime().unwrap();
         let (local_server, ws_conn) = mk_local_server(&config.server_config);
-        let (user_session, block_manager, folder_manager, local_server, grid_manager) = runtime.block_on(async {
+        let (user_session, text_block_manager, folder_manager, local_server, grid_manager) = runtime.block_on(async {
             let user_session = mk_user_session(&config, &local_server, &config.server_config);
-            let block_manager = BlockDepsResolver::resolve(
+            let text_block_manager = TextBlockDepsResolver::resolve(
                 local_server.clone(),
                 ws_conn.clone(),
                 user_session.clone(),
@@ -119,7 +119,7 @@ impl FlowySDK {
                 user_session.clone(),
                 &config.server_config,
                 &ws_conn,
-                &block_manager,
+                &text_block_manager,
                 &grid_manager,
             )
             .await;
@@ -128,11 +128,23 @@ impl FlowySDK {
                 local_server.run();
             }
             ws_conn.init().await;
-            (user_session, block_manager, folder_manager, local_server, grid_manager)
+            (
+                user_session,
+                text_block_manager,
+                folder_manager,
+                local_server,
+                grid_manager,
+            )
         });
 
         let dispatcher = Arc::new(EventDispatcher::construct(runtime, || {
-            mk_modules(&ws_conn, &folder_manager, &grid_manager, &user_session, &block_manager)
+            mk_modules(
+                &ws_conn,
+                &folder_manager,
+                &grid_manager,
+                &user_session,
+                &text_block_manager,
+            )
         }));
 
         _start_listening(&dispatcher, &ws_conn, &user_session, &folder_manager);
@@ -140,7 +152,7 @@ impl FlowySDK {
         Self {
             config,
             user_session,
-            block_manager,
+            text_block_manager,
             folder_manager,
             grid_manager,
             dispatcher,

+ 12 - 6
frontend/rust-lib/flowy-sdk/src/module.rs

@@ -1,4 +1,4 @@
-use flowy_block::BlockManager;
+use flowy_block::TextBlockManager;
 use flowy_folder::manager::FolderManager;
 use flowy_grid::manager::GridManager;
 use flowy_net::ws::connection::FlowyWebSocketConnect;
@@ -11,14 +11,20 @@ pub fn mk_modules(
     folder_manager: &Arc<FolderManager>,
     grid_manager: &Arc<GridManager>,
     user_session: &Arc<UserSession>,
-    block_manager: &Arc<BlockManager>,
+    text_block_manager: &Arc<TextBlockManager>,
 ) -> Vec<Module> {
     let user_module = mk_user_module(user_session.clone());
     let folder_module = mk_folder_module(folder_manager.clone());
     let network_module = mk_network_module(ws_conn.clone());
     let grid_module = mk_grid_module(grid_manager.clone());
-    let block_module = mk_block_module(block_manager.clone());
-    vec![user_module, folder_module, network_module, grid_module, block_module]
+    let text_block_module = mk_text_block_module(text_block_manager.clone());
+    vec![
+        user_module,
+        folder_module,
+        network_module,
+        grid_module,
+        text_block_module,
+    ]
 }
 
 fn mk_user_module(user_session: Arc<UserSession>) -> Module {
@@ -37,6 +43,6 @@ fn mk_grid_module(grid_manager: Arc<GridManager>) -> Module {
     flowy_grid::event_map::create(grid_manager)
 }
 
-fn mk_block_module(block_manager: Arc<BlockManager>) -> Module {
-    flowy_block::event_map::create(block_manager)
+fn mk_text_block_module(text_block_manager: Arc<TextBlockManager>) -> Module {
+    flowy_block::event_map::create(text_block_manager)
 }

+ 0 - 0
frontend/rust-lib/flowy-sync/src/cache/disk/folder_rev_impl.rs


+ 0 - 0
frontend/rust-lib/flowy-sync/src/cache/disk/grid_rev_impl.rs


+ 8 - 3
frontend/rust-lib/flowy-sync/src/cache/disk/mod.rs

@@ -1,9 +1,14 @@
-mod sql_impl;
+mod folder_rev_impl;
+mod grid_rev_impl;
+mod text_block_rev_impl;
+
+pub use folder_rev_impl::*;
+pub use grid_rev_impl::*;
+pub use text_block_rev_impl::*;
+
 use crate::RevisionRecord;
 use diesel::SqliteConnection;
 use flowy_collaboration::entities::revision::RevisionRange;
-pub use sql_impl::*;
-
 use flowy_error::FlowyResult;
 use std::fmt::Debug;
 

+ 3 - 3
frontend/rust-lib/flowy-sync/src/cache/disk/sql_impl.rs → frontend/rust-lib/flowy-sync/src/cache/disk/text_block_rev_impl.rs

@@ -14,12 +14,12 @@ use flowy_database::{
 use flowy_error::{internal_error, FlowyError, FlowyResult};
 use std::sync::Arc;
 
-pub struct SQLitePersistence {
+pub struct SQLiteTextBlockRevisionPersistence {
     user_id: String,
     pub(crate) pool: Arc<ConnectionPool>,
 }
 
-impl RevisionDiskCache for SQLitePersistence {
+impl RevisionDiskCache for SQLiteTextBlockRevisionPersistence {
     type Error = FlowyError;
 
     fn create_revision_records(
@@ -83,7 +83,7 @@ impl RevisionDiskCache for SQLitePersistence {
     }
 }
 
-impl SQLitePersistence {
+impl SQLiteTextBlockRevisionPersistence {
     pub(crate) fn new(user_id: &str, pool: Arc<ConnectionPool>) -> Self {
         Self {
             user_id: user_id.to_owned(),

+ 2 - 325
frontend/rust-lib/flowy-sync/src/cache/mod.rs

@@ -1,325 +1,2 @@
-mod disk;
-mod memory;
-
-use crate::cache::{
-    disk::{RevisionChangeset, RevisionDiskCache, RevisionTableState, SQLitePersistence},
-    memory::{RevisionMemoryCache, RevisionMemoryCacheDelegate},
-};
-
-use flowy_collaboration::entities::revision::{Revision, RevisionRange, RevisionState};
-use flowy_database::ConnectionPool;
-use flowy_error::{internal_error, FlowyError, FlowyResult};
-
-use crate::RevisionCompact;
-use std::collections::VecDeque;
-use std::{borrow::Cow, sync::Arc};
-use tokio::sync::RwLock;
-use tokio::task::spawn_blocking;
-
-pub const REVISION_WRITE_INTERVAL_IN_MILLIS: u64 = 600;
-
-pub struct RevisionPersistence {
-    user_id: String,
-    object_id: String,
-    disk_cache: Arc<dyn RevisionDiskCache<Error = FlowyError>>,
-    memory_cache: Arc<RevisionMemoryCache>,
-    sync_seq: RwLock<RevisionSyncSequence>,
-}
-impl RevisionPersistence {
-    pub fn new(user_id: &str, object_id: &str, pool: Arc<ConnectionPool>) -> RevisionPersistence {
-        let disk_cache = Arc::new(SQLitePersistence::new(user_id, pool));
-        let memory_cache = Arc::new(RevisionMemoryCache::new(object_id, Arc::new(disk_cache.clone())));
-        let object_id = object_id.to_owned();
-        let user_id = user_id.to_owned();
-        let sync_seq = RwLock::new(RevisionSyncSequence::new());
-        Self {
-            user_id,
-            object_id,
-            disk_cache,
-            memory_cache,
-            sync_seq,
-        }
-    }
-
-    /// Save the revision that comes from remote to disk.
-    #[tracing::instrument(level = "trace", skip(self, revision), fields(rev_id, object_id=%self.object_id), err)]
-    pub(crate) async fn add_ack_revision(&self, revision: &Revision) -> FlowyResult<()> {
-        tracing::Span::current().record("rev_id", &revision.rev_id);
-        self.add(revision.clone(), RevisionState::Ack, true).await
-    }
-
-    /// Append the revision that already existed in the local DB state to sync sequence
-    #[tracing::instrument(level = "trace", skip(self), fields(rev_id, object_id=%self.object_id), err)]
-    pub(crate) async fn sync_revision(&self, revision: &Revision) -> FlowyResult<()> {
-        tracing::Span::current().record("rev_id", &revision.rev_id);
-        self.add(revision.clone(), RevisionState::Sync, false).await?;
-        self.sync_seq.write().await.add(revision.rev_id)?;
-        Ok(())
-    }
-
-    /// Save the revision to disk and append it to the end of the sync sequence.
-    #[tracing::instrument(level = "trace", skip(self, revision), fields(rev_id, compact_range, object_id=%self.object_id), err)]
-    pub(crate) async fn add_sync_revision<C>(&self, revision: &Revision) -> FlowyResult<i64>
-    where
-        C: RevisionCompact,
-    {
-        let result = self.sync_seq.read().await.compact();
-        match result {
-            None => {
-                tracing::Span::current().record("rev_id", &revision.rev_id);
-                self.add(revision.clone(), RevisionState::Sync, true).await?;
-                self.sync_seq.write().await.add(revision.rev_id)?;
-                Ok(revision.rev_id)
-            }
-            Some((range, mut compact_seq)) => {
-                tracing::Span::current().record("compact_range", &format!("{}", range).as_str());
-                let mut revisions = self.revisions_in_range(&range).await?;
-                if range.to_rev_ids().len() != revisions.len() {
-                    debug_assert_eq!(range.to_rev_ids().len(), revisions.len());
-                }
-
-                // append the new revision
-                revisions.push(revision.clone());
-
-                // compact multiple revisions into one
-                let compact_revision = C::compact_revisions(&self.user_id, &self.object_id, revisions)?;
-                let rev_id = compact_revision.rev_id;
-                tracing::Span::current().record("rev_id", &rev_id);
-
-                // insert new revision
-                compact_seq.push_back(rev_id);
-
-                // replace the revisions in range with compact revision
-                self.compact(&range, compact_revision).await?;
-                debug_assert_eq!(self.sync_seq.read().await.len(), compact_seq.len());
-                self.sync_seq.write().await.reset(compact_seq);
-                Ok(rev_id)
-            }
-        }
-    }
-
-    /// Remove the revision with rev_id from the sync sequence.
-    pub(crate) async fn ack_revision(&self, rev_id: i64) -> FlowyResult<()> {
-        if self.sync_seq.write().await.ack(&rev_id).is_ok() {
-            self.memory_cache.ack(&rev_id).await;
-        }
-        Ok(())
-    }
-
-    pub(crate) async fn next_sync_revision(&self) -> FlowyResult<Option<Revision>> {
-        match self.sync_seq.read().await.next_rev_id() {
-            None => Ok(None),
-            Some(rev_id) => Ok(self.get(rev_id).await.map(|record| record.revision)),
-        }
-    }
-
-    /// The cache gets reset while it conflicts with the remote revisions.
-    #[tracing::instrument(level = "trace", skip(self, revisions), err)]
-    pub(crate) async fn reset(&self, revisions: Vec<Revision>) -> FlowyResult<()> {
-        let records = revisions
-            .to_vec()
-            .into_iter()
-            .map(|revision| RevisionRecord {
-                revision,
-                state: RevisionState::Sync,
-                write_to_disk: false,
-            })
-            .collect::<Vec<_>>();
-
-        let _ = self
-            .disk_cache
-            .delete_and_insert_records(&self.object_id, None, records.clone())?;
-        let _ = self.memory_cache.reset_with_revisions(records).await;
-        self.sync_seq.write().await.clear();
-        Ok(())
-    }
-
-    async fn add(&self, revision: Revision, state: RevisionState, write_to_disk: bool) -> FlowyResult<()> {
-        if self.memory_cache.contains(&revision.rev_id) {
-            tracing::warn!("Duplicate revision: {}:{}-{:?}", self.object_id, revision.rev_id, state);
-            return Ok(());
-        }
-        let record = RevisionRecord {
-            revision,
-            state,
-            write_to_disk,
-        };
-
-        self.memory_cache.add(Cow::Owned(record)).await;
-        Ok(())
-    }
-
-    async fn compact(&self, range: &RevisionRange, new_revision: Revision) -> FlowyResult<()> {
-        self.memory_cache.remove_with_range(range);
-        let rev_ids = range.to_rev_ids();
-        let _ = self
-            .disk_cache
-            .delete_revision_records(&self.object_id, Some(rev_ids))?;
-
-        self.add(new_revision, RevisionState::Sync, true).await?;
-        Ok(())
-    }
-
-    pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> {
-        match self.memory_cache.get(&rev_id).await {
-            None => match self
-                .disk_cache
-                .read_revision_records(&self.object_id, Some(vec![rev_id]))
-            {
-                Ok(mut records) => {
-                    let record = records.pop()?;
-                    assert!(records.is_empty());
-                    Some(record)
-                }
-                Err(e) => {
-                    tracing::error!("{}", e);
-                    None
-                }
-            },
-            Some(revision) => Some(revision),
-        }
-    }
-
-    pub fn batch_get(&self, doc_id: &str) -> FlowyResult<Vec<RevisionRecord>> {
-        self.disk_cache.read_revision_records(doc_id, None)
-    }
-
-    // Read the revision which rev_id >= range.start && rev_id <= range.end
-    pub async fn revisions_in_range(&self, range: &RevisionRange) -> FlowyResult<Vec<Revision>> {
-        let range = range.clone();
-        let mut records = self.memory_cache.get_with_range(&range).await?;
-        let range_len = range.len() as usize;
-        if records.len() != range_len {
-            let disk_cache = self.disk_cache.clone();
-            let object_id = self.object_id.clone();
-            records = spawn_blocking(move || disk_cache.read_revision_records_with_range(&object_id, &range))
-                .await
-                .map_err(internal_error)??;
-
-            if records.len() != range_len {
-                // #[cfg(debug_assertions)]
-                // records.iter().for_each(|record| {
-                //     let delta = PlainDelta::from_bytes(&record.revision.delta_data).unwrap();
-                //     tracing::trace!("{}", delta.to_string());
-                // });
-                tracing::error!("Expect revision len {},but receive {}", range_len, records.len());
-            }
-        }
-        Ok(records
-            .into_iter()
-            .map(|record| record.revision)
-            .collect::<Vec<Revision>>())
-    }
-}
-
-pub fn mk_revision_disk_cache(
-    user_id: &str,
-    pool: Arc<ConnectionPool>,
-) -> Arc<dyn RevisionDiskCache<Error = FlowyError>> {
-    Arc::new(SQLitePersistence::new(user_id, pool))
-}
-
-impl RevisionMemoryCacheDelegate for Arc<SQLitePersistence> {
-    #[tracing::instrument(level = "trace", skip(self, records), fields(checkpoint_result), err)]
-    fn checkpoint_tick(&self, mut records: Vec<RevisionRecord>) -> FlowyResult<()> {
-        let conn = &*self.pool.get().map_err(internal_error)?;
-        records.retain(|record| record.write_to_disk);
-        if !records.is_empty() {
-            tracing::Span::current().record(
-                "checkpoint_result",
-                &format!("{} records were saved", records.len()).as_str(),
-            );
-            let _ = self.create_revision_records(records, conn)?;
-        }
-        Ok(())
-    }
-
-    fn receive_ack(&self, object_id: &str, rev_id: i64) {
-        let changeset = RevisionChangeset {
-            object_id: object_id.to_string(),
-            rev_id: rev_id.into(),
-            state: RevisionTableState::Ack,
-        };
-        match self.update_revision_record(vec![changeset]) {
-            Ok(_) => {}
-            Err(e) => tracing::error!("{}", e),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct RevisionRecord {
-    pub revision: Revision,
-    pub state: RevisionState,
-    pub write_to_disk: bool,
-}
-
-impl RevisionRecord {
-    pub fn ack(&mut self) {
-        self.state = RevisionState::Ack;
-    }
-}
-
-#[derive(Default)]
-struct RevisionSyncSequence(VecDeque<i64>);
-impl RevisionSyncSequence {
-    fn new() -> Self {
-        RevisionSyncSequence::default()
-    }
-
-    fn add(&mut self, new_rev_id: i64) -> FlowyResult<()> {
-        // The last revision's rev_id must be greater than the new one.
-        if let Some(rev_id) = self.0.back() {
-            if *rev_id >= new_rev_id {
-                return Err(
-                    FlowyError::internal().context(format!("The new revision's id must be greater than {}", rev_id))
-                );
-            }
-        }
-        self.0.push_back(new_rev_id);
-        Ok(())
-    }
-
-    fn ack(&mut self, rev_id: &i64) -> FlowyResult<()> {
-        let cur_rev_id = self.0.front().cloned();
-        if let Some(pop_rev_id) = cur_rev_id {
-            if &pop_rev_id != rev_id {
-                let desc = format!(
-                    "The ack rev_id:{} is not equal to the current rev_id:{}",
-                    rev_id, pop_rev_id
-                );
-                return Err(FlowyError::internal().context(desc));
-            }
-            let _ = self.0.pop_front();
-        }
-        Ok(())
-    }
-
-    fn next_rev_id(&self) -> Option<i64> {
-        self.0.front().cloned()
-    }
-
-    fn reset(&mut self, new_seq: VecDeque<i64>) {
-        self.0 = new_seq;
-    }
-
-    fn clear(&mut self) {
-        self.0.clear();
-    }
-
-    fn len(&self) -> usize {
-        self.0.len()
-    }
-
-    // Compact the rev_ids into one except the current synchronizing rev_id.
-    fn compact(&self) -> Option<(RevisionRange, VecDeque<i64>)> {
-        self.next_rev_id()?;
-
-        let mut new_seq = self.0.clone();
-        let mut drained = new_seq.drain(1..).collect::<VecDeque<_>>();
-
-        let start = drained.pop_front()?;
-        let end = drained.pop_back().unwrap_or(start);
-        Some((RevisionRange { start, end }, new_seq))
-    }
-}
+pub(crate) mod disk;
+pub(crate) mod memory;

+ 2 - 0
frontend/rust-lib/flowy-sync/src/lib.rs

@@ -1,11 +1,13 @@
 mod cache;
 mod conflict_resolve;
 mod rev_manager;
+mod rev_persistence;
 mod ws_manager;
 
 pub use cache::*;
 pub use conflict_resolve::*;
 pub use rev_manager::*;
+pub use rev_persistence::*;
 pub use ws_manager::*;
 
 #[macro_use]

+ 322 - 0
frontend/rust-lib/flowy-sync/src/rev_persistence.rs

@@ -0,0 +1,322 @@
+use crate::cache::{
+    disk::{RevisionChangeset, RevisionDiskCache, RevisionTableState, SQLiteTextBlockRevisionPersistence},
+    memory::{RevisionMemoryCache, RevisionMemoryCacheDelegate},
+};
+
+use flowy_collaboration::entities::revision::{Revision, RevisionRange, RevisionState};
+use flowy_database::ConnectionPool;
+use flowy_error::{internal_error, FlowyError, FlowyResult};
+
+use crate::RevisionCompact;
+use std::collections::VecDeque;
+use std::{borrow::Cow, sync::Arc};
+use tokio::sync::RwLock;
+use tokio::task::spawn_blocking;
+
+pub const REVISION_WRITE_INTERVAL_IN_MILLIS: u64 = 600;
+
+pub struct RevisionPersistence {
+    user_id: String,
+    object_id: String,
+    disk_cache: Arc<dyn RevisionDiskCache<Error = FlowyError>>,
+    memory_cache: Arc<RevisionMemoryCache>,
+    sync_seq: RwLock<RevisionSyncSequence>,
+}
+impl RevisionPersistence {
+    pub fn new(user_id: &str, object_id: &str, pool: Arc<ConnectionPool>) -> RevisionPersistence {
+        let disk_cache = Arc::new(SQLiteTextBlockRevisionPersistence::new(user_id, pool));
+        let memory_cache = Arc::new(RevisionMemoryCache::new(object_id, Arc::new(disk_cache.clone())));
+        let object_id = object_id.to_owned();
+        let user_id = user_id.to_owned();
+        let sync_seq = RwLock::new(RevisionSyncSequence::new());
+        Self {
+            user_id,
+            object_id,
+            disk_cache,
+            memory_cache,
+            sync_seq,
+        }
+    }
+
+    /// Save the revision that comes from remote to disk.
+    #[tracing::instrument(level = "trace", skip(self, revision), fields(rev_id, object_id=%self.object_id), err)]
+    pub(crate) async fn add_ack_revision(&self, revision: &Revision) -> FlowyResult<()> {
+        tracing::Span::current().record("rev_id", &revision.rev_id);
+        self.add(revision.clone(), RevisionState::Ack, true).await
+    }
+
+    /// Append the revision that already existed in the local DB state to sync sequence
+    #[tracing::instrument(level = "trace", skip(self), fields(rev_id, object_id=%self.object_id), err)]
+    pub(crate) async fn sync_revision(&self, revision: &Revision) -> FlowyResult<()> {
+        tracing::Span::current().record("rev_id", &revision.rev_id);
+        self.add(revision.clone(), RevisionState::Sync, false).await?;
+        self.sync_seq.write().await.add(revision.rev_id)?;
+        Ok(())
+    }
+
+    /// Save the revision to disk and append it to the end of the sync sequence.
+    #[tracing::instrument(level = "trace", skip(self, revision), fields(rev_id, compact_range, object_id=%self.object_id), err)]
+    pub(crate) async fn add_sync_revision<C>(&self, revision: &Revision) -> FlowyResult<i64>
+    where
+        C: RevisionCompact,
+    {
+        let result = self.sync_seq.read().await.compact();
+        match result {
+            None => {
+                tracing::Span::current().record("rev_id", &revision.rev_id);
+                self.add(revision.clone(), RevisionState::Sync, true).await?;
+                self.sync_seq.write().await.add(revision.rev_id)?;
+                Ok(revision.rev_id)
+            }
+            Some((range, mut compact_seq)) => {
+                tracing::Span::current().record("compact_range", &format!("{}", range).as_str());
+                let mut revisions = self.revisions_in_range(&range).await?;
+                if range.to_rev_ids().len() != revisions.len() {
+                    debug_assert_eq!(range.to_rev_ids().len(), revisions.len());
+                }
+
+                // append the new revision
+                revisions.push(revision.clone());
+
+                // compact multiple revisions into one
+                let compact_revision = C::compact_revisions(&self.user_id, &self.object_id, revisions)?;
+                let rev_id = compact_revision.rev_id;
+                tracing::Span::current().record("rev_id", &rev_id);
+
+                // insert new revision
+                compact_seq.push_back(rev_id);
+
+                // replace the revisions in range with compact revision
+                self.compact(&range, compact_revision).await?;
+                debug_assert_eq!(self.sync_seq.read().await.len(), compact_seq.len());
+                self.sync_seq.write().await.reset(compact_seq);
+                Ok(rev_id)
+            }
+        }
+    }
+
+    /// Remove the revision with rev_id from the sync sequence.
+    pub(crate) async fn ack_revision(&self, rev_id: i64) -> FlowyResult<()> {
+        if self.sync_seq.write().await.ack(&rev_id).is_ok() {
+            self.memory_cache.ack(&rev_id).await;
+        }
+        Ok(())
+    }
+
+    pub(crate) async fn next_sync_revision(&self) -> FlowyResult<Option<Revision>> {
+        match self.sync_seq.read().await.next_rev_id() {
+            None => Ok(None),
+            Some(rev_id) => Ok(self.get(rev_id).await.map(|record| record.revision)),
+        }
+    }
+
+    /// The cache gets reset while it conflicts with the remote revisions.
+    #[tracing::instrument(level = "trace", skip(self, revisions), err)]
+    pub(crate) async fn reset(&self, revisions: Vec<Revision>) -> FlowyResult<()> {
+        let records = revisions
+            .to_vec()
+            .into_iter()
+            .map(|revision| RevisionRecord {
+                revision,
+                state: RevisionState::Sync,
+                write_to_disk: false,
+            })
+            .collect::<Vec<_>>();
+
+        let _ = self
+            .disk_cache
+            .delete_and_insert_records(&self.object_id, None, records.clone())?;
+        let _ = self.memory_cache.reset_with_revisions(records).await;
+        self.sync_seq.write().await.clear();
+        Ok(())
+    }
+
+    async fn add(&self, revision: Revision, state: RevisionState, write_to_disk: bool) -> FlowyResult<()> {
+        if self.memory_cache.contains(&revision.rev_id) {
+            tracing::warn!("Duplicate revision: {}:{}-{:?}", self.object_id, revision.rev_id, state);
+            return Ok(());
+        }
+        let record = RevisionRecord {
+            revision,
+            state,
+            write_to_disk,
+        };
+
+        self.memory_cache.add(Cow::Owned(record)).await;
+        Ok(())
+    }
+
+    async fn compact(&self, range: &RevisionRange, new_revision: Revision) -> FlowyResult<()> {
+        self.memory_cache.remove_with_range(range);
+        let rev_ids = range.to_rev_ids();
+        let _ = self
+            .disk_cache
+            .delete_revision_records(&self.object_id, Some(rev_ids))?;
+
+        self.add(new_revision, RevisionState::Sync, true).await?;
+        Ok(())
+    }
+
+    pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> {
+        match self.memory_cache.get(&rev_id).await {
+            None => match self
+                .disk_cache
+                .read_revision_records(&self.object_id, Some(vec![rev_id]))
+            {
+                Ok(mut records) => {
+                    let record = records.pop()?;
+                    assert!(records.is_empty());
+                    Some(record)
+                }
+                Err(e) => {
+                    tracing::error!("{}", e);
+                    None
+                }
+            },
+            Some(revision) => Some(revision),
+        }
+    }
+
+    pub fn batch_get(&self, doc_id: &str) -> FlowyResult<Vec<RevisionRecord>> {
+        self.disk_cache.read_revision_records(doc_id, None)
+    }
+
+    // Read the revision which rev_id >= range.start && rev_id <= range.end
+    pub async fn revisions_in_range(&self, range: &RevisionRange) -> FlowyResult<Vec<Revision>> {
+        let range = range.clone();
+        let mut records = self.memory_cache.get_with_range(&range).await?;
+        let range_len = range.len() as usize;
+        if records.len() != range_len {
+            let disk_cache = self.disk_cache.clone();
+            let object_id = self.object_id.clone();
+            records = spawn_blocking(move || disk_cache.read_revision_records_with_range(&object_id, &range))
+                .await
+                .map_err(internal_error)??;
+
+            if records.len() != range_len {
+                // #[cfg(debug_assertions)]
+                // records.iter().for_each(|record| {
+                //     let delta = PlainDelta::from_bytes(&record.revision.delta_data).unwrap();
+                //     tracing::trace!("{}", delta.to_string());
+                // });
+                tracing::error!("Expect revision len {},but receive {}", range_len, records.len());
+            }
+        }
+        Ok(records
+            .into_iter()
+            .map(|record| record.revision)
+            .collect::<Vec<Revision>>())
+    }
+}
+
+pub fn mk_revision_disk_cache(
+    user_id: &str,
+    pool: Arc<ConnectionPool>,
+) -> Arc<dyn RevisionDiskCache<Error = FlowyError>> {
+    Arc::new(SQLiteTextBlockRevisionPersistence::new(user_id, pool))
+}
+
+impl RevisionMemoryCacheDelegate for Arc<SQLiteTextBlockRevisionPersistence> {
+    #[tracing::instrument(level = "trace", skip(self, records), fields(checkpoint_result), err)]
+    fn checkpoint_tick(&self, mut records: Vec<RevisionRecord>) -> FlowyResult<()> {
+        let conn = &*self.pool.get().map_err(internal_error)?;
+        records.retain(|record| record.write_to_disk);
+        if !records.is_empty() {
+            tracing::Span::current().record(
+                "checkpoint_result",
+                &format!("{} records were saved", records.len()).as_str(),
+            );
+            let _ = self.create_revision_records(records, conn)?;
+        }
+        Ok(())
+    }
+
+    fn receive_ack(&self, object_id: &str, rev_id: i64) {
+        let changeset = RevisionChangeset {
+            object_id: object_id.to_string(),
+            rev_id: rev_id.into(),
+            state: RevisionTableState::Ack,
+        };
+        match self.update_revision_record(vec![changeset]) {
+            Ok(_) => {}
+            Err(e) => tracing::error!("{}", e),
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct RevisionRecord {
+    pub revision: Revision,
+    pub state: RevisionState,
+    pub write_to_disk: bool,
+}
+
+impl RevisionRecord {
+    pub fn ack(&mut self) {
+        self.state = RevisionState::Ack;
+    }
+}
+
+#[derive(Default)]
+struct RevisionSyncSequence(VecDeque<i64>);
+impl RevisionSyncSequence {
+    fn new() -> Self {
+        RevisionSyncSequence::default()
+    }
+
+    fn add(&mut self, new_rev_id: i64) -> FlowyResult<()> {
+        // The last revision's rev_id must be greater than the new one.
+        if let Some(rev_id) = self.0.back() {
+            if *rev_id >= new_rev_id {
+                return Err(
+                    FlowyError::internal().context(format!("The new revision's id must be greater than {}", rev_id))
+                );
+            }
+        }
+        self.0.push_back(new_rev_id);
+        Ok(())
+    }
+
+    fn ack(&mut self, rev_id: &i64) -> FlowyResult<()> {
+        let cur_rev_id = self.0.front().cloned();
+        if let Some(pop_rev_id) = cur_rev_id {
+            if &pop_rev_id != rev_id {
+                let desc = format!(
+                    "The ack rev_id:{} is not equal to the current rev_id:{}",
+                    rev_id, pop_rev_id
+                );
+                return Err(FlowyError::internal().context(desc));
+            }
+            let _ = self.0.pop_front();
+        }
+        Ok(())
+    }
+
+    fn next_rev_id(&self) -> Option<i64> {
+        self.0.front().cloned()
+    }
+
+    fn reset(&mut self, new_seq: VecDeque<i64>) {
+        self.0 = new_seq;
+    }
+
+    fn clear(&mut self) {
+        self.0.clear();
+    }
+
+    fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    // Compact the rev_ids into one except the current synchronizing rev_id.
+    fn compact(&self) -> Option<(RevisionRange, VecDeque<i64>)> {
+        self.next_rev_id()?;
+
+        let mut new_seq = self.0.clone();
+        let mut drained = new_seq.drain(1..).collect::<VecDeque<_>>();
+
+        let start = drained.pop_front()?;
+        let end = drained.pop_back().unwrap_or(start);
+        Some((RevisionRange { start, end }, new_seq))
+    }
+}

+ 115 - 0
shared-lib/flowy-collaboration/src/client_grid/block_pad.rs

@@ -0,0 +1,115 @@
+use crate::entities::revision::{md5, RepeatedRevision, Revision};
+use crate::errors::{internal_error, CollaborateError, CollaborateResult};
+use crate::util::{cal_diff, make_delta_from_revisions};
+use flowy_grid_data_model::entities::{BlockMeta, RowMeta, RowOrder};
+use lib_infra::uuid;
+use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
+use std::sync::Arc;
+
+pub type BlockMetaDelta = PlainTextDelta;
+pub type BlockDeltaBuilder = PlainTextDeltaBuilder;
+
+pub struct BlockMetaPad {
+    pub(crate) block_meta: Arc<BlockMeta>,
+    pub(crate) delta: BlockMetaDelta,
+}
+
+impl BlockMetaPad {
+    pub fn from_delta(delta: BlockMetaDelta) -> CollaborateResult<Self> {
+        let s = delta.to_str()?;
+        let block_delta: BlockMeta = serde_json::from_str(&s).map_err(|e| {
+            CollaborateError::internal().context(format!("Deserialize delta to block meta failed: {}", e))
+        })?;
+
+        Ok(Self {
+            block_meta: Arc::new(block_delta),
+            delta,
+        })
+    }
+
+    pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
+        let block_delta: BlockMetaDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
+        Self::from_delta(block_delta)
+    }
+
+    pub fn create_row(&mut self, row: RowMeta) -> CollaborateResult<Option<BlockMetaChange>> {
+        self.modify(|grid| {
+            grid.rows.push(row);
+            Ok(Some(()))
+        })
+    }
+
+    pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult<Option<BlockMetaChange>> {
+        self.modify(|grid| {
+            grid.rows.retain(|row| !row_ids.contains(&row.id));
+            Ok(Some(()))
+        })
+    }
+
+    pub fn md5(&self) -> String {
+        md5(&self.delta.to_bytes())
+    }
+
+    pub fn delta_str(&self) -> String {
+        self.delta.to_delta_str()
+    }
+
+    pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<BlockMetaChange>>
+    where
+        F: FnOnce(&mut BlockMeta) -> CollaborateResult<Option<()>>,
+    {
+        let cloned_meta = self.block_meta.clone();
+        match f(Arc::make_mut(&mut self.block_meta))? {
+            None => Ok(None),
+            Some(_) => {
+                let old = json_from_grid(&cloned_meta)?;
+                let new = json_from_grid(&self.block_meta)?;
+                match cal_diff::<PlainTextAttributes>(old, new) {
+                    None => Ok(None),
+                    Some(delta) => {
+                        self.delta = self.delta.compose(&delta)?;
+                        Ok(Some(BlockMetaChange { delta, md5: self.md5() }))
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn json_from_grid(block_meta: &Arc<BlockMeta>) -> CollaborateResult<String> {
+    let json = serde_json::to_string(block_meta)
+        .map_err(|err| internal_error(format!("Serialize grid to json str failed. {:?}", err)))?;
+    Ok(json)
+}
+
+pub struct BlockMetaChange {
+    pub delta: BlockMetaDelta,
+    /// md5: the md5 of the grid after applying the change.
+    pub md5: String,
+}
+
+pub fn make_block_meta_delta(block_meta: &BlockMeta) -> BlockMetaDelta {
+    let json = serde_json::to_string(&block_meta).unwrap();
+    PlainTextDeltaBuilder::new().insert(&json).build()
+}
+
+pub fn make_block_meta_revisions(user_id: &str, block_meta: &BlockMeta) -> RepeatedRevision {
+    let delta = make_block_meta_delta(block_meta);
+    let bytes = delta.to_bytes();
+    let revision = Revision::initial_revision(user_id, &block_meta.block_id, bytes);
+    revision.into()
+}
+
+impl std::default::Default for BlockMetaPad {
+    fn default() -> Self {
+        let block_meta = BlockMeta {
+            block_id: uuid(),
+            rows: vec![],
+        };
+        let delta = make_block_meta_delta(&block_meta);
+        BlockMetaPad {
+            block_meta: Arc::new(block_meta),
+            delta,
+        }
+    }
+}

+ 10 - 10
shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs

@@ -33,7 +33,7 @@ impl GridMetaPad {
 
     pub fn create_row(&mut self, row: RowMeta) -> CollaborateResult<Option<GridChange>> {
         self.modify_grid(|grid| {
-            grid.rows.push(row);
+            // grid.rows.push(row);
             Ok(Some(()))
         })
     }
@@ -47,7 +47,7 @@ impl GridMetaPad {
 
     pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult<Option<GridChange>> {
         self.modify_grid(|grid| {
-            grid.rows.retain(|row| !row_ids.contains(&row.id));
+            // grid.rows.retain(|row| !row_ids.contains(&row.id));
             Ok(Some(()))
         })
     }
@@ -74,17 +74,17 @@ impl GridMetaPad {
             .map(FieldOrder::from)
             .collect::<Vec<FieldOrder>>();
 
-        let row_orders = self
-            .grid_meta
-            .rows
-            .iter()
-            .map(RowOrder::from)
-            .collect::<Vec<RowOrder>>();
+        // let row_orders = self
+        //     .grid_meta
+        //     .rows
+        //     .iter()
+        //     .map(RowOrder::from)
+        //     .collect::<Vec<RowOrder>>();
 
         Grid {
             id: "".to_string(),
             field_orders,
-            row_orders,
+            row_orders: vec![],
         }
     }
 
@@ -147,7 +147,7 @@ impl std::default::Default for GridMetaPad {
         let grid = GridMeta {
             grid_id: uuid(),
             fields: vec![],
-            rows: vec![],
+            blocks: vec![],
         };
         let delta = make_grid_delta(&grid);
         GridMetaPad {

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

@@ -1,3 +1,5 @@
+mod block_pad;
 mod grid_pad;
 
+pub use block_pad::*;
 pub use grid_pad::*;

+ 1 - 1
shared-lib/flowy-collaboration/src/entities/mod.rs

@@ -1,5 +1,5 @@
-pub mod document_info;
 pub mod folder_info;
 pub mod parser;
 pub mod revision;
+pub mod text_block_info;
 pub mod ws_data;

+ 15 - 15
shared-lib/flowy-collaboration/src/entities/document_info.rs → shared-lib/flowy-collaboration/src/entities/text_block_info.rs

@@ -6,7 +6,7 @@ use flowy_derive::ProtoBuf;
 use lib_ot::{errors::OTError, rich_text::RichTextDelta};
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct CreateBlockParams {
+pub struct CreateTextBlockParams {
     #[pb(index = 1)]
     pub id: String,
 
@@ -15,7 +15,7 @@ pub struct CreateBlockParams {
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
-pub struct BlockInfo {
+pub struct TextBlockInfo {
     #[pb(index = 1)]
     pub block_id: String,
 
@@ -29,14 +29,14 @@ pub struct BlockInfo {
     pub base_rev_id: i64,
 }
 
-impl BlockInfo {
+impl TextBlockInfo {
     pub fn delta(&self) -> Result<RichTextDelta, OTError> {
         let delta = RichTextDelta::from_bytes(&self.text)?;
         Ok(delta)
     }
 }
 
-impl std::convert::TryFrom<Revision> for BlockInfo {
+impl std::convert::TryFrom<Revision> for TextBlockInfo {
     type Error = CollaborateError;
 
     fn try_from(revision: Revision) -> Result<Self, Self::Error> {
@@ -48,7 +48,7 @@ impl std::convert::TryFrom<Revision> for BlockInfo {
         let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
         let doc_json = delta.to_delta_str();
 
-        Ok(BlockInfo {
+        Ok(TextBlockInfo {
             block_id: revision.object_id,
             text: doc_json,
             rev_id: revision.rev_id,
@@ -58,7 +58,7 @@ impl std::convert::TryFrom<Revision> for BlockInfo {
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct ResetBlockParams {
+pub struct ResetTextBlockParams {
     #[pb(index = 1)]
     pub block_id: String,
 
@@ -67,7 +67,7 @@ pub struct ResetBlockParams {
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct BlockDelta {
+pub struct TextBlockDelta {
     #[pb(index = 1)]
     pub block_id: String,
 
@@ -88,30 +88,30 @@ pub struct NewDocUser {
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct BlockId {
+pub struct TextBlockId {
     #[pb(index = 1)]
     pub value: String,
 }
-impl AsRef<str> for BlockId {
+impl AsRef<str> for TextBlockId {
     fn as_ref(&self) -> &str {
         &self.value
     }
 }
 
-impl std::convert::From<String> for BlockId {
+impl std::convert::From<String> for TextBlockId {
     fn from(value: String) -> Self {
-        BlockId { value }
+        TextBlockId { value }
     }
 }
 
-impl std::convert::From<BlockId> for String {
-    fn from(block_id: BlockId) -> Self {
+impl std::convert::From<TextBlockId> for String {
+    fn from(block_id: TextBlockId) -> Self {
         block_id.value
     }
 }
 
-impl std::convert::From<&String> for BlockId {
+impl std::convert::From<&String> for TextBlockId {
     fn from(s: &String) -> Self {
-        BlockId { value: s.to_owned() }
+        TextBlockId { value: s.to_owned() }
     }
 }

+ 3 - 3
shared-lib/flowy-collaboration/src/protobuf/model/mod.rs

@@ -7,8 +7,8 @@ pub use folder_info::*;
 mod ws_data;
 pub use ws_data::*;
 
+mod text_block_info;
+pub use text_block_info::*;
+
 mod revision;
 pub use revision::*;
-
-mod document_info;
-pub use document_info::*;

+ 122 - 122
shared-lib/flowy-collaboration/src/protobuf/model/document_info.rs → shared-lib/flowy-collaboration/src/protobuf/model/text_block_info.rs

@@ -17,14 +17,14 @@
 #![allow(trivial_casts)]
 #![allow(unused_imports)]
 #![allow(unused_results)]
-//! Generated file from `document_info.proto`
+//! Generated file from `text_block_info.proto`
 
 /// Generated files are compatible only with the same version
 /// of protobuf runtime.
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
 
 #[derive(PartialEq,Clone,Default)]
-pub struct CreateBlockParams {
+pub struct CreateTextBlockParams {
     // message fields
     pub id: ::std::string::String,
     pub revisions: ::protobuf::SingularPtrField<super::revision::RepeatedRevision>,
@@ -33,14 +33,14 @@ pub struct CreateBlockParams {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a CreateBlockParams {
-    fn default() -> &'a CreateBlockParams {
-        <CreateBlockParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a CreateTextBlockParams {
+    fn default() -> &'a CreateTextBlockParams {
+        <CreateTextBlockParams as ::protobuf::Message>::default_instance()
     }
 }
 
-impl CreateBlockParams {
-    pub fn new() -> CreateBlockParams {
+impl CreateTextBlockParams {
+    pub fn new() -> CreateTextBlockParams {
         ::std::default::Default::default()
     }
 
@@ -104,7 +104,7 @@ impl CreateBlockParams {
     }
 }
 
-impl ::protobuf::Message for CreateBlockParams {
+impl ::protobuf::Message for CreateTextBlockParams {
     fn is_initialized(&self) -> bool {
         for v in &self.revisions {
             if !v.is_initialized() {
@@ -187,8 +187,8 @@ impl ::protobuf::Message for CreateBlockParams {
         Self::descriptor_static()
     }
 
-    fn new() -> CreateBlockParams {
-        CreateBlockParams::new()
+    fn new() -> CreateTextBlockParams {
+        CreateTextBlockParams::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -197,29 +197,29 @@ impl ::protobuf::Message for CreateBlockParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &CreateBlockParams| { &m.id },
-                |m: &mut CreateBlockParams| { &mut m.id },
+                |m: &CreateTextBlockParams| { &m.id },
+                |m: &mut CreateTextBlockParams| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::revision::RepeatedRevision>>(
                 "revisions",
-                |m: &CreateBlockParams| { &m.revisions },
-                |m: &mut CreateBlockParams| { &mut m.revisions },
+                |m: &CreateTextBlockParams| { &m.revisions },
+                |m: &mut CreateTextBlockParams| { &mut m.revisions },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateBlockParams>(
-                "CreateBlockParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateTextBlockParams>(
+                "CreateTextBlockParams",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static CreateBlockParams {
-        static instance: ::protobuf::rt::LazyV2<CreateBlockParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(CreateBlockParams::new)
+    fn default_instance() -> &'static CreateTextBlockParams {
+        static instance: ::protobuf::rt::LazyV2<CreateTextBlockParams> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(CreateTextBlockParams::new)
     }
 }
 
-impl ::protobuf::Clear for CreateBlockParams {
+impl ::protobuf::Clear for CreateTextBlockParams {
     fn clear(&mut self) {
         self.id.clear();
         self.revisions.clear();
@@ -227,20 +227,20 @@ impl ::protobuf::Clear for CreateBlockParams {
     }
 }
 
-impl ::std::fmt::Debug for CreateBlockParams {
+impl ::std::fmt::Debug for CreateTextBlockParams {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for CreateBlockParams {
+impl ::protobuf::reflect::ProtobufValue for CreateTextBlockParams {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct BlockInfo {
+pub struct TextBlockInfo {
     // message fields
     pub block_id: ::std::string::String,
     pub text: ::std::string::String,
@@ -251,14 +251,14 @@ pub struct BlockInfo {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a BlockInfo {
-    fn default() -> &'a BlockInfo {
-        <BlockInfo as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a TextBlockInfo {
+    fn default() -> &'a TextBlockInfo {
+        <TextBlockInfo as ::protobuf::Message>::default_instance()
     }
 }
 
-impl BlockInfo {
-    pub fn new() -> BlockInfo {
+impl TextBlockInfo {
+    pub fn new() -> TextBlockInfo {
         ::std::default::Default::default()
     }
 
@@ -345,7 +345,7 @@ impl BlockInfo {
     }
 }
 
-impl ::protobuf::Message for BlockInfo {
+impl ::protobuf::Message for TextBlockInfo {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -446,8 +446,8 @@ impl ::protobuf::Message for BlockInfo {
         Self::descriptor_static()
     }
 
-    fn new() -> BlockInfo {
-        BlockInfo::new()
+    fn new() -> TextBlockInfo {
+        TextBlockInfo::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -456,39 +456,39 @@ impl ::protobuf::Message for BlockInfo {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "block_id",
-                |m: &BlockInfo| { &m.block_id },
-                |m: &mut BlockInfo| { &mut m.block_id },
+                |m: &TextBlockInfo| { &m.block_id },
+                |m: &mut TextBlockInfo| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "text",
-                |m: &BlockInfo| { &m.text },
-                |m: &mut BlockInfo| { &mut m.text },
+                |m: &TextBlockInfo| { &m.text },
+                |m: &mut TextBlockInfo| { &mut m.text },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
                 "rev_id",
-                |m: &BlockInfo| { &m.rev_id },
-                |m: &mut BlockInfo| { &mut m.rev_id },
+                |m: &TextBlockInfo| { &m.rev_id },
+                |m: &mut TextBlockInfo| { &mut m.rev_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
                 "base_rev_id",
-                |m: &BlockInfo| { &m.base_rev_id },
-                |m: &mut BlockInfo| { &mut m.base_rev_id },
+                |m: &TextBlockInfo| { &m.base_rev_id },
+                |m: &mut TextBlockInfo| { &mut m.base_rev_id },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<BlockInfo>(
-                "BlockInfo",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<TextBlockInfo>(
+                "TextBlockInfo",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static BlockInfo {
-        static instance: ::protobuf::rt::LazyV2<BlockInfo> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(BlockInfo::new)
+    fn default_instance() -> &'static TextBlockInfo {
+        static instance: ::protobuf::rt::LazyV2<TextBlockInfo> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(TextBlockInfo::new)
     }
 }
 
-impl ::protobuf::Clear for BlockInfo {
+impl ::protobuf::Clear for TextBlockInfo {
     fn clear(&mut self) {
         self.block_id.clear();
         self.text.clear();
@@ -498,20 +498,20 @@ impl ::protobuf::Clear for BlockInfo {
     }
 }
 
-impl ::std::fmt::Debug for BlockInfo {
+impl ::std::fmt::Debug for TextBlockInfo {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for BlockInfo {
+impl ::protobuf::reflect::ProtobufValue for TextBlockInfo {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct ResetBlockParams {
+pub struct ResetTextBlockParams {
     // message fields
     pub block_id: ::std::string::String,
     pub revisions: ::protobuf::SingularPtrField<super::revision::RepeatedRevision>,
@@ -520,14 +520,14 @@ pub struct ResetBlockParams {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a ResetBlockParams {
-    fn default() -> &'a ResetBlockParams {
-        <ResetBlockParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a ResetTextBlockParams {
+    fn default() -> &'a ResetTextBlockParams {
+        <ResetTextBlockParams as ::protobuf::Message>::default_instance()
     }
 }
 
-impl ResetBlockParams {
-    pub fn new() -> ResetBlockParams {
+impl ResetTextBlockParams {
+    pub fn new() -> ResetTextBlockParams {
         ::std::default::Default::default()
     }
 
@@ -591,7 +591,7 @@ impl ResetBlockParams {
     }
 }
 
-impl ::protobuf::Message for ResetBlockParams {
+impl ::protobuf::Message for ResetTextBlockParams {
     fn is_initialized(&self) -> bool {
         for v in &self.revisions {
             if !v.is_initialized() {
@@ -674,8 +674,8 @@ impl ::protobuf::Message for ResetBlockParams {
         Self::descriptor_static()
     }
 
-    fn new() -> ResetBlockParams {
-        ResetBlockParams::new()
+    fn new() -> ResetTextBlockParams {
+        ResetTextBlockParams::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -684,29 +684,29 @@ impl ::protobuf::Message for ResetBlockParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "block_id",
-                |m: &ResetBlockParams| { &m.block_id },
-                |m: &mut ResetBlockParams| { &mut m.block_id },
+                |m: &ResetTextBlockParams| { &m.block_id },
+                |m: &mut ResetTextBlockParams| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::revision::RepeatedRevision>>(
                 "revisions",
-                |m: &ResetBlockParams| { &m.revisions },
-                |m: &mut ResetBlockParams| { &mut m.revisions },
+                |m: &ResetTextBlockParams| { &m.revisions },
+                |m: &mut ResetTextBlockParams| { &mut m.revisions },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ResetBlockParams>(
-                "ResetBlockParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ResetTextBlockParams>(
+                "ResetTextBlockParams",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static ResetBlockParams {
-        static instance: ::protobuf::rt::LazyV2<ResetBlockParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(ResetBlockParams::new)
+    fn default_instance() -> &'static ResetTextBlockParams {
+        static instance: ::protobuf::rt::LazyV2<ResetTextBlockParams> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(ResetTextBlockParams::new)
     }
 }
 
-impl ::protobuf::Clear for ResetBlockParams {
+impl ::protobuf::Clear for ResetTextBlockParams {
     fn clear(&mut self) {
         self.block_id.clear();
         self.revisions.clear();
@@ -714,20 +714,20 @@ impl ::protobuf::Clear for ResetBlockParams {
     }
 }
 
-impl ::std::fmt::Debug for ResetBlockParams {
+impl ::std::fmt::Debug for ResetTextBlockParams {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for ResetBlockParams {
+impl ::protobuf::reflect::ProtobufValue for ResetTextBlockParams {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct BlockDelta {
+pub struct TextBlockDelta {
     // message fields
     pub block_id: ::std::string::String,
     pub delta_str: ::std::string::String,
@@ -736,14 +736,14 @@ pub struct BlockDelta {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a BlockDelta {
-    fn default() -> &'a BlockDelta {
-        <BlockDelta as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a TextBlockDelta {
+    fn default() -> &'a TextBlockDelta {
+        <TextBlockDelta as ::protobuf::Message>::default_instance()
     }
 }
 
-impl BlockDelta {
-    pub fn new() -> BlockDelta {
+impl TextBlockDelta {
+    pub fn new() -> TextBlockDelta {
         ::std::default::Default::default()
     }
 
@@ -800,7 +800,7 @@ impl BlockDelta {
     }
 }
 
-impl ::protobuf::Message for BlockDelta {
+impl ::protobuf::Message for TextBlockDelta {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -875,8 +875,8 @@ impl ::protobuf::Message for BlockDelta {
         Self::descriptor_static()
     }
 
-    fn new() -> BlockDelta {
-        BlockDelta::new()
+    fn new() -> TextBlockDelta {
+        TextBlockDelta::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -885,29 +885,29 @@ impl ::protobuf::Message for BlockDelta {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "block_id",
-                |m: &BlockDelta| { &m.block_id },
-                |m: &mut BlockDelta| { &mut m.block_id },
+                |m: &TextBlockDelta| { &m.block_id },
+                |m: &mut TextBlockDelta| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "delta_str",
-                |m: &BlockDelta| { &m.delta_str },
-                |m: &mut BlockDelta| { &mut m.delta_str },
+                |m: &TextBlockDelta| { &m.delta_str },
+                |m: &mut TextBlockDelta| { &mut m.delta_str },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<BlockDelta>(
-                "BlockDelta",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<TextBlockDelta>(
+                "TextBlockDelta",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static BlockDelta {
-        static instance: ::protobuf::rt::LazyV2<BlockDelta> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(BlockDelta::new)
+    fn default_instance() -> &'static TextBlockDelta {
+        static instance: ::protobuf::rt::LazyV2<TextBlockDelta> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(TextBlockDelta::new)
     }
 }
 
-impl ::protobuf::Clear for BlockDelta {
+impl ::protobuf::Clear for TextBlockDelta {
     fn clear(&mut self) {
         self.block_id.clear();
         self.delta_str.clear();
@@ -915,13 +915,13 @@ impl ::protobuf::Clear for BlockDelta {
     }
 }
 
-impl ::std::fmt::Debug for BlockDelta {
+impl ::std::fmt::Debug for TextBlockDelta {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for BlockDelta {
+impl ::protobuf::reflect::ProtobufValue for TextBlockDelta {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -1164,7 +1164,7 @@ impl ::protobuf::reflect::ProtobufValue for NewDocUser {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct BlockId {
+pub struct TextBlockId {
     // message fields
     pub value: ::std::string::String,
     // special fields
@@ -1172,14 +1172,14 @@ pub struct BlockId {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a BlockId {
-    fn default() -> &'a BlockId {
-        <BlockId as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a TextBlockId {
+    fn default() -> &'a TextBlockId {
+        <TextBlockId as ::protobuf::Message>::default_instance()
     }
 }
 
-impl BlockId {
-    pub fn new() -> BlockId {
+impl TextBlockId {
+    pub fn new() -> TextBlockId {
         ::std::default::Default::default()
     }
 
@@ -1210,7 +1210,7 @@ impl BlockId {
     }
 }
 
-impl ::protobuf::Message for BlockId {
+impl ::protobuf::Message for TextBlockId {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -1276,8 +1276,8 @@ impl ::protobuf::Message for BlockId {
         Self::descriptor_static()
     }
 
-    fn new() -> BlockId {
-        BlockId::new()
+    fn new() -> TextBlockId {
+        TextBlockId::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -1286,57 +1286,57 @@ impl ::protobuf::Message for BlockId {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "value",
-                |m: &BlockId| { &m.value },
-                |m: &mut BlockId| { &mut m.value },
+                |m: &TextBlockId| { &m.value },
+                |m: &mut TextBlockId| { &mut m.value },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<BlockId>(
-                "BlockId",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<TextBlockId>(
+                "TextBlockId",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static BlockId {
-        static instance: ::protobuf::rt::LazyV2<BlockId> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(BlockId::new)
+    fn default_instance() -> &'static TextBlockId {
+        static instance: ::protobuf::rt::LazyV2<TextBlockId> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(TextBlockId::new)
     }
 }
 
-impl ::protobuf::Clear for BlockId {
+impl ::protobuf::Clear for TextBlockId {
     fn clear(&mut self) {
         self.value.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for BlockId {
+impl ::std::fmt::Debug for TextBlockId {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for BlockId {
+impl ::protobuf::reflect::ProtobufValue for TextBlockId {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x13document_info.proto\x1a\x0erevision.proto\"T\n\x11CreateBlockParam\
-    s\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12/\n\trevisions\x18\x02\
-    \x20\x01(\x0b2\x11.RepeatedRevisionR\trevisions\"q\n\tBlockInfo\x12\x19\
-    \n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12\x12\n\x04text\x18\x02\
-    \x20\x01(\tR\x04text\x12\x15\n\x06rev_id\x18\x03\x20\x01(\x03R\x05revId\
-    \x12\x1e\n\x0bbase_rev_id\x18\x04\x20\x01(\x03R\tbaseRevId\"^\n\x10Reset\
-    BlockParams\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12/\n\
-    \trevisions\x18\x02\x20\x01(\x0b2\x11.RepeatedRevisionR\trevisions\"D\n\
-    \nBlockDelta\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12\
-    \x1b\n\tdelta_str\x18\x02\x20\x01(\tR\x08deltaStr\"S\n\nNewDocUser\x12\
-    \x17\n\x07user_id\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\x06rev_id\x18\
-    \x02\x20\x01(\x03R\x05revId\x12\x15\n\x06doc_id\x18\x03\x20\x01(\tR\x05d\
-    ocId\"\x1f\n\x07BlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05valueb\
-    \x06proto3\
+    \n\x15text_block_info.proto\x1a\x0erevision.proto\"X\n\x15CreateTextBloc\
+    kParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12/\n\trevisions\x18\
+    \x02\x20\x01(\x0b2\x11.RepeatedRevisionR\trevisions\"u\n\rTextBlockInfo\
+    \x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12\x12\n\x04text\
+    \x18\x02\x20\x01(\tR\x04text\x12\x15\n\x06rev_id\x18\x03\x20\x01(\x03R\
+    \x05revId\x12\x1e\n\x0bbase_rev_id\x18\x04\x20\x01(\x03R\tbaseRevId\"b\n\
+    \x14ResetTextBlockParams\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07bl\
+    ockId\x12/\n\trevisions\x18\x02\x20\x01(\x0b2\x11.RepeatedRevisionR\trev\
+    isions\"H\n\x0eTextBlockDelta\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\
+    \x07blockId\x12\x1b\n\tdelta_str\x18\x02\x20\x01(\tR\x08deltaStr\"S\n\nN\
+    ewDocUser\x12\x17\n\x07user_id\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\
+    \x06rev_id\x18\x02\x20\x01(\x03R\x05revId\x12\x15\n\x06doc_id\x18\x03\
+    \x20\x01(\tR\x05docId\"#\n\x0bTextBlockId\x12\x14\n\x05value\x18\x01\x20\
+    \x01(\tR\x05valueb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 5 - 5
shared-lib/flowy-collaboration/src/protobuf/proto/document_info.proto → shared-lib/flowy-collaboration/src/protobuf/proto/text_block_info.proto

@@ -1,21 +1,21 @@
 syntax = "proto3";
 import "revision.proto";
 
-message CreateBlockParams {
+message CreateTextBlockParams {
     string id = 1;
     RepeatedRevision revisions = 2;
 }
-message BlockInfo {
+message TextBlockInfo {
     string block_id = 1;
     string text = 2;
     int64 rev_id = 3;
     int64 base_rev_id = 4;
 }
-message ResetBlockParams {
+message ResetTextBlockParams {
     string block_id = 1;
     RepeatedRevision revisions = 2;
 }
-message BlockDelta {
+message TextBlockDelta {
     string block_id = 1;
     string delta_str = 2;
 }
@@ -24,6 +24,6 @@ message NewDocUser {
     int64 rev_id = 2;
     string doc_id = 3;
 }
-message BlockId {
+message TextBlockId {
     string value = 1;
 }

+ 5 - 5
shared-lib/flowy-collaboration/src/server_document/document_manager.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::{document_info::BlockInfo, ws_data::ServerRevisionWSDataBuilder},
+    entities::{text_block_info::TextBlockInfo, ws_data::ServerRevisionWSDataBuilder},
     errors::{internal_error, CollaborateError, CollaborateResult},
     protobuf::{ClientRevisionWSData, RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB},
     server_document::document_pad::ServerDocument,
@@ -18,13 +18,13 @@ use tokio::{
 };
 
 pub trait DocumentCloudPersistence: Send + Sync + Debug {
-    fn read_document(&self, doc_id: &str) -> BoxResultFuture<BlockInfo, CollaborateError>;
+    fn read_document(&self, doc_id: &str) -> BoxResultFuture<TextBlockInfo, CollaborateError>;
 
     fn create_document(
         &self,
         doc_id: &str,
         repeated_revision: RepeatedRevisionPB,
-    ) -> BoxResultFuture<Option<BlockInfo>, CollaborateError>;
+    ) -> BoxResultFuture<Option<TextBlockInfo>, CollaborateError>;
 
     fn read_document_revisions(
         &self,
@@ -181,7 +181,7 @@ impl ServerDocumentManager {
         }
     }
 
-    async fn create_document_handler(&self, doc: BlockInfo) -> Result<Arc<OpenDocumentHandler>, CollaborateError> {
+    async fn create_document_handler(&self, doc: TextBlockInfo) -> Result<Arc<OpenDocumentHandler>, CollaborateError> {
         let persistence = self.persistence.clone();
         let handle = spawn_blocking(|| OpenDocumentHandler::new(doc, persistence))
             .await
@@ -205,7 +205,7 @@ struct OpenDocumentHandler {
 }
 
 impl OpenDocumentHandler {
-    fn new(doc: BlockInfo, persistence: Arc<dyn DocumentCloudPersistence>) -> Result<Self, CollaborateError> {
+    fn new(doc: TextBlockInfo, persistence: Arc<dyn DocumentCloudPersistence>) -> Result<Self, CollaborateError> {
         let doc_id = doc.block_id.clone();
         let (sender, receiver) = mpsc::channel(1000);
         let users = DashMap::new();

+ 7 - 7
shared-lib/flowy-collaboration/src/util.rs

@@ -1,13 +1,13 @@
 use crate::{
     entities::{
-        document_info::BlockInfo,
         folder_info::{FolderDelta, FolderInfo},
         revision::{RepeatedRevision, Revision},
+        text_block_info::TextBlockInfo,
     },
     errors::{CollaborateError, CollaborateResult},
     protobuf::{
-        BlockInfo as BlockInfoPB, FolderInfo as FolderInfoPB, RepeatedRevision as RepeatedRevisionPB,
-        Revision as RevisionPB,
+        FolderInfo as FolderInfoPB, RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB,
+        TextBlockInfo as TextBlockInfoPB,
     },
 };
 use dissimilar::Chunk;
@@ -202,11 +202,11 @@ pub fn make_folder_pb_from_revisions_pb(
 pub fn make_document_info_from_revisions_pb(
     doc_id: &str,
     revisions: RepeatedRevisionPB,
-) -> Result<Option<BlockInfo>, CollaborateError> {
+) -> Result<Option<TextBlockInfo>, CollaborateError> {
     match make_document_info_pb_from_revisions_pb(doc_id, revisions)? {
         None => Ok(None),
         Some(pb) => {
-            let document_info: BlockInfo = pb.try_into().map_err(|e| {
+            let document_info: TextBlockInfo = pb.try_into().map_err(|e| {
                 CollaborateError::internal().context(format!("Deserialize document info from pb failed: {}", e))
             })?;
             Ok(Some(document_info))
@@ -218,7 +218,7 @@ pub fn make_document_info_from_revisions_pb(
 pub fn make_document_info_pb_from_revisions_pb(
     doc_id: &str,
     mut revisions: RepeatedRevisionPB,
-) -> Result<Option<BlockInfoPB>, CollaborateError> {
+) -> Result<Option<TextBlockInfoPB>, CollaborateError> {
     let revisions = revisions.take_items();
     if revisions.is_empty() {
         return Ok(None);
@@ -240,7 +240,7 @@ pub fn make_document_info_pb_from_revisions_pb(
     }
 
     let text = document_delta.to_delta_str();
-    let mut block_info = BlockInfoPB::new();
+    let mut block_info = TextBlockInfoPB::new();
     block_info.set_block_id(doc_id.to_owned());
     block_info.set_text(text);
     block_info.set_base_rev_id(base_rev_id);

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

@@ -1,8 +1,6 @@
 use crate::entities::{Field, RowMeta};
-use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use serde::{Deserialize, Serialize};
+use flowy_derive::ProtoBuf;
 use std::collections::HashMap;
-use strum_macros::{Display, EnumIter, EnumString};
 
 pub const DEFAULT_ROW_HEIGHT: i32 = 36;
 pub const DEFAULT_FIELD_WIDTH: i32 = 150;

+ 14 - 3
shared-lib/flowy-grid-data-model/src/entities/meta.rs

@@ -1,4 +1,3 @@
-use crate::entities::Row;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
@@ -16,14 +15,26 @@ pub struct GridMeta {
     pub fields: Vec<Field>,
 
     #[pb(index = 3)]
-    pub rows: Vec<RowMeta>,
+    pub blocks: Vec<Block>,
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
-pub struct GridBlock {
+pub struct Block {
     #[pb(index = 1)]
     pub id: String,
 
+    #[pb(index = 2)]
+    pub start_row_index: i32,
+
+    #[pb(index = 3)]
+    pub row_count: i32,
+}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
+pub struct BlockMeta {
+    #[pb(index = 1)]
+    pub block_id: String,
+
     #[pb(index = 2)]
     pub rows: Vec<RowMeta>,
 }

+ 308 - 77
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<Field>,
-    pub rows: ::protobuf::RepeatedField<RowMeta>,
+    pub blocks: ::protobuf::RepeatedField<Block>,
     // 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 .RowMeta rows = 3;
+    // repeated .Block blocks = 3;
 
 
-    pub fn get_rows(&self) -> &[RowMeta] {
-        &self.rows
+    pub fn get_blocks(&self) -> &[Block] {
+        &self.blocks
     }
-    pub fn clear_rows(&mut self) {
-        self.rows.clear();
+    pub fn clear_blocks(&mut self) {
+        self.blocks.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_rows(&mut self, v: ::protobuf::RepeatedField<RowMeta>) {
-        self.rows = v;
+    pub fn set_blocks(&mut self, v: ::protobuf::RepeatedField<Block>) {
+        self.blocks = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_rows(&mut self) -> &mut ::protobuf::RepeatedField<RowMeta> {
-        &mut self.rows
+    pub fn mut_blocks(&mut self) -> &mut ::protobuf::RepeatedField<Block> {
+        &mut self.blocks
     }
 
     // Take field
-    pub fn take_rows(&mut self) -> ::protobuf::RepeatedField<RowMeta> {
-        ::std::mem::replace(&mut self.rows, ::protobuf::RepeatedField::new())
+    pub fn take_blocks(&mut self) -> ::protobuf::RepeatedField<Block> {
+        ::std::mem::replace(&mut self.blocks, ::protobuf::RepeatedField::new())
     }
 }
 
@@ -129,7 +129,7 @@ impl ::protobuf::Message for GridMeta {
                 return false;
             }
         };
-        for v in &self.rows {
+        for v in &self.blocks {
             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.rows)?;
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.blocks)?;
                 },
                 _ => {
                     ::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.rows {
+        for value in &self.blocks {
             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.rows {
+        for v in &self.blocks {
             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 GridMeta {
                 |m: &GridMeta| { &m.fields },
                 |m: &mut GridMeta| { &mut m.fields },
             ));
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowMeta>>(
-                "rows",
-                |m: &GridMeta| { &m.rows },
-                |m: &mut GridMeta| { &mut m.rows },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Block>>(
+                "blocks",
+                |m: &GridMeta| { &m.blocks },
+                |m: &mut GridMeta| { &mut m.blocks },
             ));
             ::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.rows.clear();
+        self.blocks.clear();
         self.unknown_fields.clear();
     }
 }
@@ -281,23 +281,24 @@ impl ::protobuf::reflect::ProtobufValue for GridMeta {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct GridBlock {
+pub struct Block {
     // message fields
     pub id: ::std::string::String,
-    pub rows: ::protobuf::RepeatedField<RowMeta>,
+    pub start_row_index: i32,
+    pub row_count: i32,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a GridBlock {
-    fn default() -> &'a GridBlock {
-        <GridBlock as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a Block {
+    fn default() -> &'a Block {
+        <Block as ::protobuf::Message>::default_instance()
     }
 }
 
-impl GridBlock {
-    pub fn new() -> GridBlock {
+impl Block {
+    pub fn new() -> Block {
         ::std::default::Default::default()
     }
 
@@ -327,6 +328,234 @@ impl GridBlock {
         ::std::mem::replace(&mut self.id, ::std::string::String::new())
     }
 
+    // int32 start_row_index = 2;
+
+
+    pub fn get_start_row_index(&self) -> i32 {
+        self.start_row_index
+    }
+    pub fn clear_start_row_index(&mut self) {
+        self.start_row_index = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_start_row_index(&mut self, v: i32) {
+        self.start_row_index = v;
+    }
+
+    // int32 row_count = 3;
+
+
+    pub fn get_row_count(&self) -> i32 {
+        self.row_count
+    }
+    pub fn clear_row_count(&mut self) {
+        self.row_count = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_row_count(&mut self, v: i32) {
+        self.row_count = v;
+    }
+}
+
+impl ::protobuf::Message for Block {
+    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.id)?;
+                },
+                2 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int32()?;
+                    self.start_row_index = tmp;
+                },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int32()?;
+                    self.row_count = tmp;
+                },
+                _ => {
+                    ::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.id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.id);
+        }
+        if self.start_row_index != 0 {
+            my_size += ::protobuf::rt::value_size(2, self.start_row_index, ::protobuf::wire_format::WireTypeVarint);
+        }
+        if self.row_count != 0 {
+            my_size += ::protobuf::rt::value_size(3, self.row_count, ::protobuf::wire_format::WireTypeVarint);
+        }
+        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.id.is_empty() {
+            os.write_string(1, &self.id)?;
+        }
+        if self.start_row_index != 0 {
+            os.write_int32(2, self.start_row_index)?;
+        }
+        if self.row_count != 0 {
+            os.write_int32(3, self.row_count)?;
+        }
+        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() -> Block {
+        Block::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>(
+                "id",
+                |m: &Block| { &m.id },
+                |m: &mut Block| { &mut m.id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>(
+                "start_row_index",
+                |m: &Block| { &m.start_row_index },
+                |m: &mut Block| { &mut m.start_row_index },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>(
+                "row_count",
+                |m: &Block| { &m.row_count },
+                |m: &mut Block| { &mut m.row_count },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<Block>(
+                "Block",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static Block {
+        static instance: ::protobuf::rt::LazyV2<Block> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(Block::new)
+    }
+}
+
+impl ::protobuf::Clear for Block {
+    fn clear(&mut self) {
+        self.id.clear();
+        self.start_row_index = 0;
+        self.row_count = 0;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for Block {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for Block {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct BlockMeta {
+    // message fields
+    pub block_id: ::std::string::String,
+    pub rows: ::protobuf::RepeatedField<RowMeta>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a BlockMeta {
+    fn default() -> &'a BlockMeta {
+        <BlockMeta as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl BlockMeta {
+    pub fn new() -> BlockMeta {
+        ::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())
+    }
+
     // repeated .RowMeta rows = 2;
 
 
@@ -353,7 +582,7 @@ impl GridBlock {
     }
 }
 
-impl ::protobuf::Message for GridBlock {
+impl ::protobuf::Message for BlockMeta {
     fn is_initialized(&self) -> bool {
         for v in &self.rows {
             if !v.is_initialized() {
@@ -368,7 +597,7 @@ impl ::protobuf::Message for GridBlock {
             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.id)?;
+                    ::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)?;
@@ -385,8 +614,8 @@ impl ::protobuf::Message for GridBlock {
     #[allow(unused_variables)]
     fn compute_size(&self) -> u32 {
         let mut my_size = 0;
-        if !self.id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.id);
+        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();
@@ -398,8 +627,8 @@ impl ::protobuf::Message for GridBlock {
     }
 
     fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.id.is_empty() {
-            os.write_string(1, &self.id)?;
+        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)?;
@@ -436,8 +665,8 @@ impl ::protobuf::Message for GridBlock {
         Self::descriptor_static()
     }
 
-    fn new() -> GridBlock {
-        GridBlock::new()
+    fn new() -> BlockMeta {
+        BlockMeta::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -445,44 +674,44 @@ impl ::protobuf::Message for GridBlock {
         descriptor.get(|| {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "id",
-                |m: &GridBlock| { &m.id },
-                |m: &mut GridBlock| { &mut m.id },
+                "block_id",
+                |m: &BlockMeta| { &m.block_id },
+                |m: &mut BlockMeta| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowMeta>>(
                 "rows",
-                |m: &GridBlock| { &m.rows },
-                |m: &mut GridBlock| { &mut m.rows },
+                |m: &BlockMeta| { &m.rows },
+                |m: &mut BlockMeta| { &mut m.rows },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlock>(
-                "GridBlock",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<BlockMeta>(
+                "BlockMeta",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static GridBlock {
-        static instance: ::protobuf::rt::LazyV2<GridBlock> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(GridBlock::new)
+    fn default_instance() -> &'static BlockMeta {
+        static instance: ::protobuf::rt::LazyV2<BlockMeta> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(BlockMeta::new)
     }
 }
 
-impl ::protobuf::Clear for GridBlock {
+impl ::protobuf::Clear for BlockMeta {
     fn clear(&mut self) {
-        self.id.clear();
+        self.block_id.clear();
         self.rows.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for GridBlock {
+impl ::std::fmt::Debug for BlockMeta {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for GridBlock {
+impl ::protobuf::reflect::ProtobufValue for BlockMeta {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -1997,34 +2226,36 @@ impl ::protobuf::reflect::ProtobufValue for FieldType {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\nmeta.proto\"a\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
+    \n\nmeta.proto\"c\n\x08GridMeta\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
     \x06gridId\x12\x1e\n\x06fields\x18\x02\x20\x03(\x0b2\x06.FieldR\x06field\
-    s\x12\x1c\n\x04rows\x18\x03\x20\x03(\x0b2\x08.RowMetaR\x04rows\"9\n\tGri\
-    dBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x1c\n\x04rows\x18\
-    \x02\x20\x03(\x0b2\x08.RowMetaR\x04rows\"\xe5\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_typ\
-    e\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\x12+\n\
-    \x0ctype_options\x18\x08\x20\x01(\x0b2\x08.AnyDataR\x0btypeOptions\"-\n\
-    \rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05i\
-    tems\"8\n\x07AnyData\x12\x17\n\x07type_id\x18\x01\x20\x01(\tR\x06typeId\
-    \x12\x14\n\x05value\x18\x02\x20\x01(\x0cR\x05value\"\xfd\x01\n\x07RowMet\
-    a\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x17\n\x07grid_id\x18\x02\
-    \x20\x01(\tR\x06gridId\x12D\n\x10cell_by_field_id\x18\x03\x20\x03(\x0b2\
-    \x1b.RowMeta.CellByFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\
-    \x04\x20\x01(\x05R\x06height\x12\x1e\n\nvisibility\x18\x05\x20\x01(\x08R\
-    \nvisibility\x1aK\n\x12CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\
-    \x01(\tR\x03key\x12\x1f\n\x05value\x18\x02\x20\x01(\x0b2\t.CellMetaR\x05\
-    value:\x028\x01\"\x82\x01\n\x08CellMeta\x12\x0e\n\x02id\x18\x01\x20\x01(\
-    \tR\x02id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08\
-    field_id\x18\x03\x20\x01(\tR\x07fieldId\x12\x1c\n\x04data\x18\x04\x20\
-    \x01(\x0b2\x08.AnyDataR\x04data\x12\x16\n\x06height\x18\x05\x20\x01(\x05\
-    R\x06height*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\
+    s\x12\x1e\n\x06blocks\x18\x03\x20\x03(\x0b2\x06.BlockR\x06blocks\"\\\n\
+    \x05Block\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12&\n\x0fstart_row_\
+    index\x18\x02\x20\x01(\x05R\rstartRowIndex\x12\x1b\n\trow_count\x18\x03\
+    \x20\x01(\x05R\x08rowCount\"D\n\tBlockMeta\x12\x19\n\x08block_id\x18\x01\
+    \x20\x01(\tR\x07blockId\x12\x1c\n\x04rows\x18\x02\x20\x03(\x0b2\x08.RowM\
+    etaR\x04rows\"\xe5\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\x12+\n\x0ctype_options\x18\x08\x20\
+    \x01(\x0b2\x08.AnyDataR\x0btypeOptions\"-\n\rRepeatedField\x12\x1c\n\x05\
+    items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"8\n\x07AnyData\x12\x17\
+    \n\x07type_id\x18\x01\x20\x01(\tR\x06typeId\x12\x14\n\x05value\x18\x02\
+    \x20\x01(\x0cR\x05value\"\xfd\x01\n\x07RowMeta\x12\x0e\n\x02id\x18\x01\
+    \x20\x01(\tR\x02id\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\
+    \x12D\n\x10cell_by_field_id\x18\x03\x20\x03(\x0b2\x1b.RowMeta.CellByFiel\
+    dIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x04\x20\x01(\x05R\x06he\
+    ight\x12\x1e\n\nvisibility\x18\x05\x20\x01(\x08R\nvisibility\x1aK\n\x12C\
+    ellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1f\n\
+    \x05value\x18\x02\x20\x01(\x0b2\t.CellMetaR\x05value:\x028\x01\"\x82\x01\
+    \n\x08CellMeta\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x15\n\x06ro\
+    w_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\
+    \tR\x07fieldId\x12\x1c\n\x04data\x18\x04\x20\x01(\x0b2\x08.AnyDataR\x04d\
+    ata\x12\x16\n\x06height\x18\x05\x20\x01(\x05R\x06height*d\n\tFieldType\
+    \x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08Date\
+    Time\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;

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

@@ -3,10 +3,15 @@ syntax = "proto3";
 message GridMeta {
     string grid_id = 1;
     repeated Field fields = 2;
-    repeated RowMeta rows = 3;
+    repeated Block blocks = 3;
 }
-message GridBlock {
+message Block {
     string id = 1;
+    int32 start_row_index = 2;
+    int32 row_count = 3;
+}
+message BlockMeta {
+    string block_id = 1;
     repeated RowMeta rows = 2;
 }
 message Field {

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

@@ -7,14 +7,14 @@ fn grid_serde_test() {
     let grid = GridMeta {
         grid_id,
         fields,
-        rows: vec![],
+        blocks: vec![],
     };
 
     let grid_1_json = serde_json::to_string(&grid).unwrap();
-    let _: Grid = serde_json::from_str(&grid_1_json).unwrap();
+    let _: GridMeta = serde_json::from_str(&grid_1_json).unwrap();
     assert_eq!(
         grid_1_json,
-        r#"{"id":"1","fields":[{"id":"1","name":"Text Field","desc":"","field_type":"RichText","frozen":false,"visibility":true,"width":150,"type_options":{"type_id":"","value":[]}}],"rows":[]}"#
+        r#"{"id":"1","fields":[{"id":"1","name":"Text Field","desc":"","field_type":"RichText","frozen":false,"visibility":true,"width":150,"type_options":{"type_id":"","value":[]}}],"blocks":[]}"#
     )
 }
 
@@ -24,11 +24,11 @@ fn grid_default_serde_test() {
     let grid = GridMeta {
         grid_id,
         fields: vec![],
-        rows: vec![],
+        blocks: vec![],
     };
 
     let json = serde_json::to_string(&grid).unwrap();
-    assert_eq!(json, r#"{"id":"1","fields":[],"row_orders":[]}"#)
+    assert_eq!(json, r#"{"id":"1","fields":[],"blocks":[]}"#)
 }
 
 fn create_field(field_id: &str) -> Field {