Parcourir la source

chore: update grid test

appflowy il y a 3 ans
Parent
commit
47081f3095
32 fichiers modifiés avec 742 ajouts et 215 suppressions
  1. 8 8
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pb.dart
  2. 4 4
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart
  3. 73 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pb.dart
  4. 12 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/meta.pbjson.dart
  5. 1 0
      frontend/rust-lib/Cargo.lock
  6. 2 2
      frontend/rust-lib/flowy-block/src/queue.rs
  7. 4 2
      frontend/rust-lib/flowy-folder/src/manager.rs
  8. 2 2
      frontend/rust-lib/flowy-folder/src/services/folder_editor.rs
  9. 1 1
      frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs
  10. 7 6
      frontend/rust-lib/flowy-folder/src/services/view/controller.rs
  11. 34 1
      frontend/rust-lib/flowy-grid/src/manager.rs
  12. 2 2
      frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs
  13. 8 4
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  14. 4 5
      frontend/rust-lib/flowy-grid/src/util.rs
  15. 24 25
      frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs
  16. 97 13
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  17. 1 0
      frontend/rust-lib/flowy-sdk/Cargo.toml
  18. 36 25
      frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
  19. 2 2
      frontend/rust-lib/flowy-sync/src/conflict_resolve.rs
  20. 4 4
      frontend/rust-lib/flowy-test/src/helper.rs
  21. 3 2
      shared-lib/flowy-collaboration/src/client_document/document_pad.rs
  22. 1 1
      shared-lib/flowy-collaboration/src/client_folder/folder_pad.rs
  23. 2 2
      shared-lib/flowy-collaboration/src/client_grid/block_pad.rs
  24. 27 53
      shared-lib/flowy-collaboration/src/client_grid/grid_builder.rs
  25. 15 2
      shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs
  26. 2 2
      shared-lib/flowy-folder-data-model/src/entities/view.rs
  27. 38 38
      shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs
  28. 2 2
      shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto
  29. 33 2
      shared-lib/flowy-grid-data-model/src/entities/meta.rs
  30. 287 4
      shared-lib/flowy-grid-data-model/src/protobuf/model/meta.rs
  31. 5 0
      shared-lib/flowy-grid-data-model/src/protobuf/proto/meta.proto
  32. 1 1
      shared-lib/lib-ot/src/core/delta/delta.rs

+ 8 - 8
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pb.dart

@@ -276,7 +276,7 @@ class CreateViewPayload extends $pb.GeneratedMessage {
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
     ..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.TextBlock, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
     ..a<$core.int>(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pluginType', $pb.PbFieldType.O3)
-    ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
+    ..a<$core.List<$core.int>>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
     ..hasRequiredFields = false
   ;
 
@@ -288,7 +288,7 @@ class CreateViewPayload extends $pb.GeneratedMessage {
     $core.String? thumbnail,
     ViewDataType? dataType,
     $core.int? pluginType,
-    $core.String? data,
+    $core.List<$core.int>? data,
   }) {
     final _result = create();
     if (belongToId != null) {
@@ -393,9 +393,9 @@ class CreateViewPayload extends $pb.GeneratedMessage {
   void clearPluginType() => clearField(6);
 
   @$pb.TagNumber(7)
-  $core.String get data => $_getSZ(6);
+  $core.List<$core.int> get data => $_getN(6);
   @$pb.TagNumber(7)
-  set data($core.String v) { $_setString(6, v); }
+  set data($core.List<$core.int> v) { $_setBytes(6, v); }
   @$pb.TagNumber(7)
   $core.bool hasData() => $_has(6);
   @$pb.TagNumber(7)
@@ -410,7 +410,7 @@ class CreateViewParams extends $pb.GeneratedMessage {
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
     ..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.TextBlock, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
     ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
-    ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
+    ..a<$core.List<$core.int>>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
     ..a<$core.int>(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pluginType', $pb.PbFieldType.O3)
     ..hasRequiredFields = false
   ;
@@ -423,7 +423,7 @@ class CreateViewParams extends $pb.GeneratedMessage {
     $core.String? thumbnail,
     ViewDataType? dataType,
     $core.String? viewId,
-    $core.String? data,
+    $core.List<$core.int>? data,
     $core.int? pluginType,
   }) {
     final _result = create();
@@ -529,9 +529,9 @@ class CreateViewParams extends $pb.GeneratedMessage {
   void clearViewId() => clearField(6);
 
   @$pb.TagNumber(7)
-  $core.String get data => $_getSZ(6);
+  $core.List<$core.int> get data => $_getN(6);
   @$pb.TagNumber(7)
-  set data($core.String v) { $_setString(6, v); }
+  set data($core.List<$core.int> v) { $_setBytes(6, v); }
   @$pb.TagNumber(7)
   $core.bool hasData() => $_has(6);
   @$pb.TagNumber(7)

+ 4 - 4
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart

@@ -60,7 +60,7 @@ const CreateViewPayload$json = const {
     const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'thumbnail'},
     const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
     const {'1': 'plugin_type', '3': 6, '4': 1, '5': 5, '10': 'pluginType'},
-    const {'1': 'data', '3': 7, '4': 1, '5': 9, '10': 'data'},
+    const {'1': 'data', '3': 7, '4': 1, '5': 12, '10': 'data'},
   ],
   '8': const [
     const {'1': 'one_of_thumbnail'},
@@ -68,7 +68,7 @@ const CreateViewPayload$json = const {
 };
 
 /// Descriptor for `CreateViewPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createViewPayloadDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UGF5bG9hZBIgCgxiZWxvbmdfdG9faWQYASABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgAUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIfCgtwbHVnaW5fdHlwZRgGIAEoBVIKcGx1Z2luVHlwZRISCgRkYXRhGAcgASgJUgRkYXRhQhIKEG9uZV9vZl90aHVtYm5haWw=');
+final $typed_data.Uint8List createViewPayloadDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UGF5bG9hZBIgCgxiZWxvbmdfdG9faWQYASABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgAUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIfCgtwbHVnaW5fdHlwZRgGIAEoBVIKcGx1Z2luVHlwZRISCgRkYXRhGAcgASgMUgRkYXRhQhIKEG9uZV9vZl90aHVtYm5haWw=');
 @$core.Deprecated('Use createViewParamsDescriptor instead')
 const CreateViewParams$json = const {
   '1': 'CreateViewParams',
@@ -79,13 +79,13 @@ const CreateViewParams$json = const {
     const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '10': 'thumbnail'},
     const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
     const {'1': 'view_id', '3': 6, '4': 1, '5': 9, '10': 'viewId'},
-    const {'1': 'data', '3': 7, '4': 1, '5': 9, '10': 'data'},
+    const {'1': 'data', '3': 7, '4': 1, '5': 12, '10': 'data'},
     const {'1': 'plugin_type', '3': 8, '4': 1, '5': 5, '10': 'pluginType'},
   ],
 };
 
 /// Descriptor for `CreateViewParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIXCgd2aWV3X2lkGAYgASgJUgZ2aWV3SWQSEgoEZGF0YRgHIAEoCVIEZGF0YRIfCgtwbHVnaW5fdHlwZRgIIAEoBVIKcGx1Z2luVHlwZQ==');
+final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSKgoJZGF0YV90eXBlGAUgASgOMg0uVmlld0RhdGFUeXBlUghkYXRhVHlwZRIXCgd2aWV3X2lkGAYgASgJUgZ2aWV3SWQSEgoEZGF0YRgHIAEoDFIEZGF0YRIfCgtwbHVnaW5fdHlwZRgIIAEoBVIKcGx1Z2luVHlwZQ==');
 @$core.Deprecated('Use viewIdDescriptor instead')
 const ViewId$json = const {
   '1': 'ViewId',

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

@@ -1003,3 +1003,76 @@ class CellMetaChangeset extends $pb.GeneratedMessage {
   void clearData() => clearField(3);
 }
 
+class BuildGridContext extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BuildGridContext', createEmptyInstance: create)
+    ..pc<FieldMeta>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldMetas', $pb.PbFieldType.PM, subBuilder: FieldMeta.create)
+    ..aOM<GridBlock>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridBlock', subBuilder: GridBlock.create)
+    ..aOM<GridBlockMeta>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridBlockMeta', subBuilder: GridBlockMeta.create)
+    ..hasRequiredFields = false
+  ;
+
+  BuildGridContext._() : super();
+  factory BuildGridContext({
+    $core.Iterable<FieldMeta>? fieldMetas,
+    GridBlock? gridBlock,
+    GridBlockMeta? gridBlockMeta,
+  }) {
+    final _result = create();
+    if (fieldMetas != null) {
+      _result.fieldMetas.addAll(fieldMetas);
+    }
+    if (gridBlock != null) {
+      _result.gridBlock = gridBlock;
+    }
+    if (gridBlockMeta != null) {
+      _result.gridBlockMeta = gridBlockMeta;
+    }
+    return _result;
+  }
+  factory BuildGridContext.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory BuildGridContext.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')
+  BuildGridContext clone() => BuildGridContext()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  BuildGridContext copyWith(void Function(BuildGridContext) updates) => super.copyWith((message) => updates(message as BuildGridContext)) as BuildGridContext; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static BuildGridContext create() => BuildGridContext._();
+  BuildGridContext createEmptyInstance() => create();
+  static $pb.PbList<BuildGridContext> createRepeated() => $pb.PbList<BuildGridContext>();
+  @$core.pragma('dart2js:noInline')
+  static BuildGridContext getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BuildGridContext>(create);
+  static BuildGridContext? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.List<FieldMeta> get fieldMetas => $_getList(0);
+
+  @$pb.TagNumber(2)
+  GridBlock get gridBlock => $_getN(1);
+  @$pb.TagNumber(2)
+  set gridBlock(GridBlock v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasGridBlock() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGridBlock() => clearField(2);
+  @$pb.TagNumber(2)
+  GridBlock ensureGridBlock() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  GridBlockMeta get gridBlockMeta => $_getN(2);
+  @$pb.TagNumber(3)
+  set gridBlockMeta(GridBlockMeta v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasGridBlockMeta() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearGridBlockMeta() => clearField(3);
+  @$pb.TagNumber(3)
+  GridBlockMeta ensureGridBlockMeta() => $_ensure(2);
+}
+

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

@@ -191,3 +191,15 @@ const CellMetaChangeset$json = const {
 
 /// Descriptor for `CellMetaChangeset`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List cellMetaChangesetDescriptor = $convert.base64Decode('ChFDZWxsTWV0YUNoYW5nZXNldBIVCgZyb3dfaWQYASABKAlSBXJvd0lkEhkKCGZpZWxkX2lkGAIgASgJUgdmaWVsZElkEhQKBGRhdGEYAyABKAlIAFIEZGF0YUINCgtvbmVfb2ZfZGF0YQ==');
+@$core.Deprecated('Use buildGridContextDescriptor instead')
+const BuildGridContext$json = const {
+  '1': 'BuildGridContext',
+  '2': const [
+    const {'1': 'field_metas', '3': 1, '4': 3, '5': 11, '6': '.FieldMeta', '10': 'fieldMetas'},
+    const {'1': 'grid_block', '3': 2, '4': 1, '5': 11, '6': '.GridBlock', '10': 'gridBlock'},
+    const {'1': 'grid_block_meta', '3': 3, '4': 1, '5': 11, '6': '.GridBlockMeta', '10': 'gridBlockMeta'},
+  ],
+};
+
+/// Descriptor for `BuildGridContext`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List buildGridContextDescriptor = $convert.base64Decode('ChBCdWlsZEdyaWRDb250ZXh0EisKC2ZpZWxkX21ldGFzGAEgAygLMgouRmllbGRNZXRhUgpmaWVsZE1ldGFzEikKCmdyaWRfYmxvY2sYAiABKAsyCi5HcmlkQmxvY2tSCWdyaWRCbG9jaxI2Cg9ncmlkX2Jsb2NrX21ldGEYAyABKAsyDi5HcmlkQmxvY2tNZXRhUg1ncmlkQmxvY2tNZXRh');

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

@@ -1147,6 +1147,7 @@ dependencies = [
  "flowy-database",
  "flowy-folder",
  "flowy-grid",
+ "flowy-grid-data-model",
  "flowy-net",
  "flowy-sync",
  "flowy-user",

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

@@ -175,7 +175,7 @@ impl EditBlockQueue {
     }
 
     async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> {
-        let delta_data = delta.to_bytes();
+        let delta_data = delta.to_delta_bytes();
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
         let user_id = self.user.user_id()?;
         let revision = Revision::new(
@@ -198,7 +198,7 @@ pub(crate) struct TextBlockRevisionCompactor();
 impl RevisionCompactor for TextBlockRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
         let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?;
-        Ok(delta.to_bytes())
+        Ok(delta.to_delta_bytes())
     }
 }
 

+ 4 - 2
frontend/rust-lib/flowy-folder/src/manager.rs

@@ -245,9 +245,11 @@ pub trait ViewDataProcessor {
 
     fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
 
-    fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError>;
+    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
 
-    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<String, FlowyError>;
+    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError>;
+
+    fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError>;
 
     fn data_type(&self) -> ViewDataType;
 }

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

@@ -71,7 +71,7 @@ impl ClientFolderEditor {
     pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> {
         let FolderChange { delta, md5 } = change;
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_bytes();
+        let delta_data = delta.to_delta_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -128,6 +128,6 @@ struct FolderRevisionCompactor();
 impl RevisionCompactor for FolderRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
         let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_bytes())
+        Ok(delta.to_delta_bytes())
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs

@@ -115,7 +115,7 @@ impl FolderPersistence {
 
     pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> {
         let pool = self.database.db_pool()?;
-        let delta_data = initial_folder_delta(&folder)?.to_bytes();
+        let delta_data = initial_folder_delta(&folder)?.to_delta_bytes();
         let md5 = folder.md5();
         let revision = Revision::new(folder_id.as_ref(), 0, 0, delta_data, user_id, md5);
         let record = RevisionRecord {

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

@@ -55,13 +55,14 @@ impl ViewController {
     #[tracing::instrument(level = "trace", skip(self, params), fields(name = %params.name), err)]
     pub(crate) async fn create_view_from_params(&self, mut params: CreateViewParams) -> Result<View, FlowyError> {
         let processor = self.get_data_processor(&params.data_type)?;
-
+        let user_id = self.user.user_id()?;
         if params.data.is_empty() {
-            let user_id = self.user.user_id()?;
             let view_data = processor.create_default_view(&user_id, &params.view_id).await?;
-            params.data = view_data;
+            params.data = view_data.to_vec();
         } else {
-            let delta_data = Bytes::from(params.data.clone());
+            let delta_data = processor
+                .process_create_view_data(&user_id, &params.view_id, params.data.clone())
+                .await?;
             let _ = self
                 .create_view(&params.view_id, params.data_type.clone(), delta_data)
                 .await?;
@@ -162,14 +163,14 @@ impl ViewController {
             .await?;
 
         let processor = self.get_data_processor(&view.data_type)?;
-        let delta_str = processor.delta_str(view_id).await?;
+        let delta_bytes = processor.delta_bytes(view_id).await?;
         let duplicate_params = CreateViewParams {
             belong_to_id: view.belong_to_id.clone(),
             name: format!("{} (copy)", &view.name),
             desc: view.desc,
             thumbnail: view.thumbnail,
             data_type: view.data_type,
-            data: delta_str,
+            data: delta_bytes.to_vec(),
             view_id: uuid(),
             plugin_type: view.plugin_type,
         };

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

@@ -1,8 +1,11 @@
 use crate::services::grid_editor::ClientGridEditor;
 use crate::services::kv_persistence::GridKVPersistence;
+use bytes::Bytes;
 use dashmap::DashMap;
-use flowy_collaboration::entities::revision::RepeatedRevision;
+use flowy_collaboration::client_grid::{make_block_meta_delta, make_grid_delta};
+use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
 use flowy_error::{FlowyError, FlowyResult};
+use flowy_grid_data_model::entities::{BuildGridContext, GridMeta};
 use flowy_sync::disk::{SQLiteGridBlockMetaRevisionPersistence, SQLiteGridRevisionPersistence};
 use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket};
 use lib_sqlite::ConnectionPool;
@@ -172,3 +175,33 @@ impl GridEditorMap {
         self.inner.remove(grid_id);
     }
 }
+
+pub async fn make_grid_view_data(
+    user_id: &str,
+    view_id: &str,
+    grid_manager: Arc<GridManager>,
+    build_context: BuildGridContext,
+) -> FlowyResult<Bytes> {
+    let block_id = build_context.grid_block.id.clone();
+    let grid_meta = GridMeta {
+        grid_id: view_id.to_string(),
+        fields: build_context.field_metas,
+        blocks: vec![build_context.grid_block],
+    };
+
+    let grid_meta_delta = make_grid_delta(&grid_meta);
+    let grid_delta_data = grid_meta_delta.to_delta_bytes();
+    let repeated_revision: RepeatedRevision =
+        Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
+    let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
+
+    let grid_block_meta_delta = make_block_meta_delta(&build_context.grid_block_meta);
+    let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
+    let repeated_revision: RepeatedRevision =
+        Revision::initial_revision(&user_id, &block_id, block_meta_delta_data).into();
+    let _ = grid_manager
+        .create_grid_block_meta(&block_id, repeated_revision)
+        .await?;
+
+    Ok(grid_delta_data)
+}

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

@@ -280,7 +280,7 @@ impl ClientGridBlockMetaEditor {
         let GridBlockMetaChange { delta, md5 } = change;
         let user_id = self.user_id.clone();
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_bytes();
+        let delta_data = delta.to_delta_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -323,6 +323,6 @@ struct GridBlockMetaRevisionCompactor();
 impl RevisionCompactor for GridBlockMetaRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
         let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_bytes())
+        Ok(delta.to_delta_bytes())
     }
 }

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

@@ -61,6 +61,10 @@ impl ClientGridEditor {
         Ok(())
     }
 
+    pub async fn contain_field(&self, field_meta: &FieldMeta) -> bool {
+        self.grid_meta_pad.read().await.contain_field(&field_meta.id)
+    }
+
     pub async fn update_field(&self, change: FieldChangeset) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.update_field(change)?)).await?;
         Ok(())
@@ -177,8 +181,8 @@ impl ClientGridEditor {
         Ok(grid_blocks)
     }
 
-    pub async fn delta_str(&self) -> String {
-        self.grid_meta_pad.read().await.delta_str()
+    pub async fn delta_bytes(&self) -> Bytes {
+        self.grid_meta_pad.read().await.delta_bytes()
     }
 
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
@@ -199,7 +203,7 @@ impl ClientGridEditor {
         let GridChange { delta, md5 } = change;
         let user_id = self.user.user_id()?;
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_bytes();
+        let delta_data = delta.to_delta_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -256,6 +260,6 @@ struct GridRevisionCompactor();
 impl RevisionCompactor for GridRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
         let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_bytes())
+        Ok(delta.to_delta_bytes())
     }
 }

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

@@ -1,9 +1,9 @@
 use crate::services::cell::*;
 use crate::services::field::*;
-use flowy_collaboration::client_grid::{BuildGridInfo, GridBuilder};
-use flowy_grid_data_model::entities::FieldType;
+use flowy_collaboration::client_grid::GridBuilder;
+use flowy_grid_data_model::entities::{BuildGridContext, FieldType};
 
-pub fn make_default_grid(grid_id: &str) -> BuildGridInfo {
+pub fn make_default_grid() -> BuildGridContext {
     let text_field = FieldBuilder::new(RichTextTypeOptionsBuilder::default())
         .name("Name")
         .visibility(true)
@@ -20,12 +20,11 @@ pub fn make_default_grid(grid_id: &str) -> BuildGridInfo {
         .field_type(FieldType::SingleSelect)
         .build();
 
-    GridBuilder::new(grid_id)
+    GridBuilder::default()
         .add_field(text_field)
         .add_field(single_select_field)
         .add_empty_row()
         .add_empty_row()
         .add_empty_row()
         .build()
-        .unwrap()
 }

+ 24 - 25
frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs

@@ -4,57 +4,56 @@ use flowy_grid::services::cell::*;
 use flowy_grid::services::row::{deserialize_cell_data, serialize_cell_data, CellDataSerde, CreateRowContextBuilder};
 use flowy_grid_data_model::entities::{FieldChangeset, FieldType, GridBlock, GridBlockChangeset, RowMetaChangeset};
 
-#[tokio::test]
-async fn default_grid_test() {
-    let scripts = vec![AssertFieldCount(2), AssertGridMetaPad];
-    GridEditorTest::new().await.run_scripts(scripts).await;
-}
-
 #[tokio::test]
 async fn grid_create_field() {
+    let mut test = GridEditorTest::new().await;
     let text_field = create_text_field();
     let single_select_field = create_single_select_field();
+
     let scripts = vec![
-        AssertFieldCount(2),
         CreateField {
             field_meta: text_field.clone(),
         },
         AssertFieldEqual {
-            field_index: 2,
+            field_index: test.field_count,
             field_meta: text_field,
         },
-        AssertFieldCount(3),
+    ];
+    test.run_scripts(scripts).await;
+
+    let scripts = vec![
         CreateField {
             field_meta: single_select_field.clone(),
         },
         AssertFieldEqual {
-            field_index: 3,
+            field_index: test.field_count,
             field_meta: single_select_field,
         },
-        AssertFieldCount(4),
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_create_duplicate_field() {
+    let mut test = GridEditorTest::new().await;
     let text_field = create_text_field();
+    let field_count = test.field_count;
+    let expected_field_count = field_count + 1;
     let scripts = vec![
-        AssertFieldCount(2),
         CreateField {
             field_meta: text_field.clone(),
         },
-        AssertFieldCount(3),
         CreateField {
             field_meta: text_field.clone(),
         },
-        AssertFieldCount(3),
+        AssertFieldCount(expected_field_count),
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_update_field_with_empty_change() {
+    let mut test = GridEditorTest::new().await;
     let single_select_field = create_single_select_field();
     let changeset = FieldChangeset {
         field_id: single_select_field.id.clone(),
@@ -73,21 +72,21 @@ async fn grid_update_field_with_empty_change() {
         },
         UpdateField { changeset },
         AssertFieldEqual {
-            field_index: 2,
+            field_index: test.field_count,
             field_meta: single_select_field,
         },
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_update_field() {
+    let mut test = GridEditorTest::new().await;
     let single_select_field = create_single_select_field();
     let mut cloned_field = single_select_field.clone();
 
     let mut single_select_type_options = SingleSelectDescription::from(&single_select_field);
     single_select_type_options.options.push(SelectOption::new("Unknown"));
-
     let changeset = FieldChangeset {
         field_id: single_select_field.id.clone(),
         name: None,
@@ -109,26 +108,26 @@ async fn grid_update_field() {
         },
         UpdateField { changeset },
         AssertFieldEqual {
-            field_index: 2,
+            field_index: test.field_count,
             field_meta: cloned_field,
         },
-        AssertGridMetaPad,
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_delete_field() {
+    let mut test = GridEditorTest::new().await;
+    let expected_field_count = test.field_count;
     let text_field = create_text_field();
     let scripts = vec![
         CreateField {
             field_meta: text_field.clone(),
         },
-        AssertFieldCount(3),
         DeleteField { field_meta: text_field },
-        AssertFieldCount(2),
+        AssertFieldCount(expected_field_count),
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]

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

@@ -1,15 +1,22 @@
+use bytes::Bytes;
+use flowy_collaboration::client_grid::GridBuilder;
+use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
+use flowy_error::FlowyResult;
+use flowy_grid::manager::{make_grid_view_data, GridManager};
 use flowy_grid::services::cell::*;
 use flowy_grid::services::field::*;
 use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
 use flowy_grid::services::row::CreateRowContext;
 use flowy_grid_data_model::entities::{
-    CellMetaChangeset, FieldChangeset, FieldMeta, FieldType, GridBlock, GridBlockChangeset, RowMeta, RowMetaChangeset,
+    BuildGridContext, CellMetaChangeset, FieldChangeset, FieldMeta, FieldType, GridBlock, GridBlockChangeset, RowMeta,
+    RowMetaChangeset,
 };
 use flowy_sync::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_test::helper::ViewTest;
 use flowy_test::FlowySDKTest;
 use std::sync::Arc;
 use std::time::Duration;
+use strum::{EnumCount, IntoEnumIterator};
 use tokio::time::sleep;
 
 pub enum EditorScript {
@@ -71,19 +78,18 @@ pub struct GridEditorTest {
     pub field_metas: Vec<FieldMeta>,
     pub grid_blocks: Vec<GridBlock>,
     pub row_metas: Vec<Arc<RowMeta>>,
+    pub field_count: usize,
 }
 
 impl GridEditorTest {
     pub async fn new() -> Self {
-        Self::with_data("".to_owned()).await
-    }
-
-    pub async fn with_data(data: String) -> Self {
         let sdk = FlowySDKTest::default();
         let _ = sdk.init_user().await;
-        let test = ViewTest::new_grid_view(&sdk, data).await;
+        let build_context = make_template_1_grid();
+        let view_data: Bytes = build_context.try_into().unwrap();
+        let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
-        let fields = editor.get_field_metas(None).await.unwrap();
+        let field_metas = editor.get_field_metas(None).await.unwrap();
         let grid_blocks = editor.get_blocks().await.unwrap();
         let row_metas = editor.get_row_metas(None).await.unwrap();
 
@@ -92,9 +98,10 @@ impl GridEditorTest {
             sdk,
             grid_id,
             editor,
-            field_metas: fields,
+            field_metas,
             grid_blocks,
             row_metas,
+            field_count: FieldType::COUNT,
         }
     }
 
@@ -111,22 +118,30 @@ impl GridEditorTest {
         let _cache = rev_manager.revision_cache().await;
 
         match script {
-            EditorScript::CreateField { field_meta: field } => {
-                self.editor.create_field(field).await.unwrap();
+            EditorScript::CreateField { field_meta } => {
+                if !self.editor.contain_field(&field_meta).await {
+                    self.field_count += 1;
+                }
+                self.editor.create_field(field_meta).await.unwrap();
                 self.field_metas = self.editor.get_field_metas(None).await.unwrap();
+                assert_eq!(self.field_count, self.field_metas.len());
             }
             EditorScript::UpdateField { changeset: change } => {
                 self.editor.update_field(change).await.unwrap();
                 self.field_metas = self.editor.get_field_metas(None).await.unwrap();
             }
-            EditorScript::DeleteField { field_meta: field } => {
-                self.editor.delete_field(&field.id).await.unwrap();
+            EditorScript::DeleteField { field_meta } => {
+                if self.editor.contain_field(&field_meta).await {
+                    self.field_count -= 1;
+                }
+
+                self.editor.delete_field(&field_meta.id).await.unwrap();
                 self.field_metas = self.editor.get_field_metas(None).await.unwrap();
+                assert_eq!(self.field_count, self.field_metas.len());
             }
             EditorScript::AssertFieldCount(count) => {
                 assert_eq!(self.editor.get_field_metas(None).await.unwrap().len(), count);
             }
-
             EditorScript::AssertFieldEqual {
                 field_index,
                 field_meta,
@@ -220,3 +235,72 @@ pub fn create_single_select_field() -> FieldMeta {
         .field_type(FieldType::SingleSelect)
         .build()
 }
+
+fn make_template_1_grid() -> BuildGridContext {
+    let text_field = FieldBuilder::new(RichTextTypeOptionsBuilder::default())
+        .name("Name")
+        .visibility(true)
+        .field_type(FieldType::RichText)
+        .build();
+
+    // Single Select
+    let single_select = SingleSelectTypeOptionsBuilder::default()
+        .option(SelectOption::new("Live"))
+        .option(SelectOption::new("Completed"))
+        .option(SelectOption::new("Planned"))
+        .option(SelectOption::new("Paused"));
+    let single_select_field = FieldBuilder::new(single_select)
+        .name("Status")
+        .visibility(true)
+        .field_type(FieldType::SingleSelect)
+        .build();
+
+    // MultiSelect
+    let multi_select = MultiSelectTypeOptionsBuilder::default()
+        .option(SelectOption::new("Google"))
+        .option(SelectOption::new("Facebook"))
+        .option(SelectOption::new("Twitter"));
+    let multi_select_field = FieldBuilder::new(multi_select)
+        .name("Platform")
+        .visibility(true)
+        .field_type(FieldType::MultiSelect)
+        .build();
+
+    // Number
+    let number = NumberTypeOptionsBuilder::default().set_format(NumberFormat::USD);
+    let number_field = FieldBuilder::new(number)
+        .name("Price")
+        .visibility(true)
+        .field_type(FieldType::Number)
+        .build();
+
+    // Date
+    let date = DateTypeOptionsBuilder::default()
+        .date_format(DateFormat::US)
+        .time_format(TimeFormat::TwentyFourHour);
+    let date_field = FieldBuilder::new(date)
+        .name("Time")
+        .visibility(true)
+        .field_type(FieldType::DateTime)
+        .build();
+
+    // Checkbox
+    let checkbox = CheckboxTypeOptionsBuilder::default();
+    let checkbox_field = FieldBuilder::new(checkbox)
+        .name("is done")
+        .visibility(true)
+        .field_type(FieldType::Checkbox)
+        .build();
+
+    GridBuilder::default()
+        .add_field(text_field)
+        .add_field(single_select_field)
+        .add_field(multi_select_field)
+        .add_field(number_field)
+        .add_field(date_field)
+        .add_field(checkbox_field)
+        .add_empty_row()
+        .add_empty_row()
+        .add_empty_row()
+        .build()
+}

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

@@ -12,6 +12,7 @@ flowy-user = { path = "../flowy-user" }
 flowy-net = { path = "../flowy-net" }
 flowy-folder = { path = "../flowy-folder", default-features = false }
 flowy-grid = { path = "../flowy-grid", default-features = false }
+flowy-grid-data-model = { path = "../../../shared-lib/flowy-grid-data-model" }
 flowy-database = { path = "../flowy-database" }
 flowy-block = { path = "../flowy-block", default-features = false }
 flowy-sync = { path = "../flowy-sync" }

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

@@ -4,6 +4,7 @@ use flowy_collaboration::client_document::default::initial_quill_delta_string;
 use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
 use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
 use flowy_database::ConnectionPool;
+use flowy_folder::errors::FlowyResult;
 use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
 use flowy_folder::prelude::ViewDataType;
 use flowy_folder::{
@@ -11,8 +12,9 @@ use flowy_folder::{
     event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
     manager::FolderManager,
 };
-use flowy_grid::manager::GridManager;
+use flowy_grid::manager::{make_grid_view_data, GridManager};
 use flowy_grid::util::make_default_grid;
+use flowy_grid_data_model::entities::BuildGridContext;
 use flowy_net::ClientServerConfiguration;
 use flowy_net::{
     http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
@@ -23,6 +25,7 @@ use futures_core::future::BoxFuture;
 use lib_infra::future::{BoxResultFuture, FutureResult};
 use lib_ws::{WSChannel, WSMessageReceiver, WebSocketRawMessage};
 use std::collections::HashMap;
+use std::convert::TryFrom;
 use std::{convert::TryInto, sync::Arc};
 
 pub struct FolderDepsResolver();
@@ -168,29 +171,39 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
         })
     }
 
-    fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError> {
+    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
         let view_id = view_id.to_string();
         let manager = self.0.clone();
         FutureResult::new(async move {
             let editor = manager.open_block(view_id).await?;
-            let delta_str = editor.delta_str().await?;
-            Ok(delta_str)
+            let delta_bytes = Bytes::from(editor.delta_str().await?);
+            Ok(delta_bytes)
         })
     }
 
-    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<String, FlowyError> {
+    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError> {
         let user_id = user_id.to_string();
         let view_id = view_id.to_string();
         let manager = self.0.clone();
         FutureResult::new(async move {
             let view_data = initial_quill_delta_string();
-            let delta_data = Bytes::from(view_data.clone());
-            let repeated_revision: RepeatedRevision = Revision::initial_revision(&user_id, &view_id, delta_data).into();
+            let delta_data = Bytes::from(view_data);
+            let repeated_revision: RepeatedRevision =
+                Revision::initial_revision(&user_id, &view_id, delta_data.clone()).into();
             let _ = manager.create_block(view_id, repeated_revision).await?;
-            Ok(view_data)
+            Ok(delta_data)
         })
     }
 
+    fn process_create_view_data(
+        &self,
+        _user_id: &str,
+        _view_id: &str,
+        data: Vec<u8>,
+    ) -> FutureResult<Bytes, FlowyError> {
+        FutureResult::new(async move { Ok(Bytes::from(data)) })
+    }
+
     fn data_type(&self) -> ViewDataType {
         ViewDataType::TextBlock
     }
@@ -230,36 +243,34 @@ impl ViewDataProcessor for GridViewDataProcessor {
         })
     }
 
-    fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError> {
+    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
         FutureResult::new(async move {
             let editor = grid_manager.open_grid(view_id).await?;
-            let delta_str = editor.delta_str().await;
-            Ok(delta_str)
+            let delta_bytes = editor.delta_bytes().await;
+            Ok(delta_bytes)
         })
     }
 
-    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<String, FlowyError> {
-        let info = make_default_grid(view_id);
+    fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError> {
+        let build_context = make_default_grid();
         let user_id = user_id.to_string();
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
 
-        FutureResult::new(async move {
-            let grid_delta_data = Bytes::from(info.grid_delta.to_delta_str());
-            let repeated_revision: RepeatedRevision =
-                Revision::initial_revision(&user_id, &view_id, grid_delta_data).into();
-            let _ = grid_manager.create_grid(&view_id, repeated_revision).await?;
+        FutureResult::new(async move { make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await })
+    }
 
-            let block_meta_delta_data = Bytes::from(info.grid_block_meta_delta.to_delta_str());
-            let repeated_revision: RepeatedRevision =
-                Revision::initial_revision(&user_id, &info.block_id, block_meta_delta_data).into();
-            let _ = grid_manager
-                .create_grid_block_meta(&info.block_id, repeated_revision)
-                .await?;
+    fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError> {
+        let user_id = user_id.to_string();
+        let view_id = view_id.to_string();
+        let grid_manager = self.0.clone();
 
-            Ok(info.grid_delta.to_delta_str())
+        FutureResult::new(async move {
+            let bytes = Bytes::from(data);
+            let build_context = BuildGridContext::try_from(bytes)?;
+            make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await
         })
     }
 

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

@@ -154,7 +154,7 @@ where
         &rev_manager.object_id,
         base_rev_id,
         rev_id,
-        client_delta.to_bytes(),
+        client_delta.to_delta_bytes(),
         user_id,
         md5.clone(),
     );
@@ -166,7 +166,7 @@ where
                 &rev_manager.object_id,
                 base_rev_id,
                 rev_id,
-                server_delta.to_bytes(),
+                server_delta.to_delta_bytes(),
                 user_id,
                 md5,
             );

+ 4 - 4
frontend/rust-lib/flowy-test/src/helper.rs

@@ -26,7 +26,7 @@ pub struct ViewTest {
 
 impl ViewTest {
     #[allow(dead_code)]
-    pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataType, data: String) -> Self {
+    pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataType, data: Vec<u8>) -> Self {
         let workspace = create_workspace(sdk, "Workspace", "").await;
         open_workspace(sdk, &workspace.id).await;
         let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
@@ -39,12 +39,12 @@ impl ViewTest {
         }
     }
 
-    pub async fn new_grid_view(sdk: &FlowySDKTest, data: String) -> Self {
+    pub async fn new_grid_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
         Self::new(sdk, ViewDataType::Grid, data).await
     }
 
     pub async fn new_text_block_view(sdk: &FlowySDKTest) -> Self {
-        Self::new(sdk, ViewDataType::TextBlock, "".to_owned()).await
+        Self::new(sdk, ViewDataType::TextBlock, vec![]).await
     }
 }
 
@@ -91,7 +91,7 @@ async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &s
     app
 }
 
-async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataType, data: String) -> View {
+async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataType, data: Vec<u8>) -> View {
     let request = CreateViewPayload {
         belong_to_id: app_id.to_string(),
         name: "View A".to_string(),

+ 3 - 2
shared-lib/flowy-collaboration/src/client_document/document_pad.rs

@@ -6,6 +6,7 @@ use crate::{
     },
     errors::CollaborateError,
 };
+use bytes::Bytes;
 use lib_ot::{
     core::*,
     rich_text::{RichTextAttribute, RichTextDelta},
@@ -62,8 +63,8 @@ impl ClientDocument {
         self.delta.to_delta_str()
     }
 
-    pub fn to_bytes(&self) -> Vec<u8> {
-        self.delta.clone().to_bytes().to_vec()
+    pub fn to_bytes(&self) -> Bytes {
+        self.delta.to_delta_bytes()
     }
 
     pub fn to_plain_string(&self) -> String {

+ 1 - 1
shared-lib/flowy-collaboration/src/client_folder/folder_pad.rs

@@ -268,7 +268,7 @@ impl FolderPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_bytes())
+        md5(&self.delta.to_delta_bytes())
     }
 
     pub fn to_json(&self) -> CollaborateResult<String> {

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

@@ -144,7 +144,7 @@ impl GridBlockMetaPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_bytes())
+        md5(&self.delta.to_delta_bytes())
     }
 
     pub fn delta_str(&self) -> String {
@@ -165,7 +165,7 @@ pub fn make_block_meta_delta(block_meta: &GridBlockMeta) -> GridBlockMetaDelta {
 
 pub fn make_block_meta_revisions(user_id: &str, block_meta: &GridBlockMeta) -> RepeatedRevision {
     let delta = make_block_meta_delta(block_meta);
-    let bytes = delta.to_bytes();
+    let bytes = delta.to_delta_bytes();
     let revision = Revision::initial_revision(user_id, &block_meta.block_id, bytes);
     revision.into()
 }

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

@@ -1,66 +1,37 @@
 use crate::client_grid::{make_block_meta_delta, make_grid_delta, GridBlockMetaDelta, GridMetaDelta};
 use crate::errors::{CollaborateError, CollaborateResult};
-use flowy_grid_data_model::entities::{FieldMeta, GridBlock, GridBlockMeta, GridMeta, RowMeta};
+use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, GridBlock, GridBlockMeta, GridMeta, RowMeta};
 
 pub struct GridBuilder {
-    grid_id: String,
-    fields: Vec<FieldMeta>,
-    grid_block: GridBlock,
-    grid_block_meta: GridBlockMeta,
+    build_context: BuildGridContext,
 }
 
-impl GridBuilder {
-    pub fn new(grid_id: &str) -> Self {
-        let grid_block = GridBlock::new();
-        let grid_block_meta = GridBlockMeta {
-            block_id: grid_block.id.clone(),
-            rows: vec![],
-        };
-
+impl std::default::Default for GridBuilder {
+    fn default() -> Self {
         Self {
-            grid_id: grid_id.to_owned(),
-            fields: vec![],
-            grid_block,
-            grid_block_meta,
+            build_context: Default::default(),
         }
     }
+}
 
+impl GridBuilder {
     pub fn add_field(mut self, field: FieldMeta) -> Self {
-        self.fields.push(field);
+        self.build_context.field_metas.push(field);
         self
     }
 
     pub fn add_empty_row(mut self) -> Self {
-        let row = RowMeta::new(&self.grid_block.id);
-        self.grid_block_meta.rows.push(row);
-        self.grid_block.row_count += 1;
+        let row = RowMeta::new(&self.build_context.grid_block.id);
+        self.build_context.grid_block_meta.rows.push(row);
+        self.build_context.grid_block.row_count += 1;
         self
     }
 
-    pub fn build(self) -> CollaborateResult<BuildGridInfo> {
-        let block_id = self.grid_block.id.clone();
-        let grid_meta = GridMeta {
-            grid_id: self.grid_id,
-            fields: self.fields,
-            blocks: vec![self.grid_block],
-        };
-        // let _ = check_rows(&self.fields, &self.rows)?;
-        let grid_delta = make_grid_delta(&grid_meta);
-        let grid_block_meta_delta = make_block_meta_delta(&self.grid_block_meta);
-        Ok(BuildGridInfo {
-            grid_delta,
-            block_id,
-            grid_block_meta_delta,
-        })
+    pub fn build(self) -> BuildGridContext {
+        self.build_context
     }
 }
 
-pub struct BuildGridInfo {
-    pub grid_delta: GridMetaDelta,
-    pub block_id: String,
-    pub grid_block_meta_delta: GridBlockMetaDelta,
-}
-
 #[allow(dead_code)]
 fn check_rows(fields: &[FieldMeta], rows: &[RowMeta]) -> CollaborateResult<()> {
     let field_ids = fields.iter().map(|field| &field.id).collect::<Vec<&String>>();
@@ -76,28 +47,31 @@ fn check_rows(fields: &[FieldMeta], rows: &[RowMeta]) -> CollaborateResult<()> {
 
 #[cfg(test)]
 mod tests {
-    use crate::client_grid::GridBuilder;
+
+    use crate::client_grid::{make_block_meta_delta, make_grid_delta, GridBuilder};
     use flowy_grid_data_model::entities::{FieldMeta, FieldType, GridBlockMeta, GridMeta};
 
     #[test]
     fn create_default_grid_test() {
-        let info = GridBuilder::new("1")
+        let grid_id = "1".to_owned();
+        let build_context = GridBuilder::default()
             .add_field(FieldMeta::new("Name", "", FieldType::RichText))
             .add_field(FieldMeta::new("Tags", "", FieldType::SingleSelect))
             .add_empty_row()
             .add_empty_row()
             .add_empty_row()
-            .build()
-            .unwrap();
+            .build();
 
-        let grid_meta: GridMeta = serde_json::from_str(&info.grid_delta.to_str().unwrap()).unwrap();
-        assert_eq!(grid_meta.fields.len(), 2);
-        assert_eq!(grid_meta.blocks.len(), 1);
+        let grid_meta = GridMeta {
+            grid_id: grid_id.clone(),
+            fields: build_context.field_metas,
+            blocks: vec![build_context.grid_block],
+        };
 
-        let grid_block_meta: GridBlockMeta =
-            serde_json::from_str(&info.grid_block_meta_delta.to_str().unwrap()).unwrap();
-        assert_eq!(grid_block_meta.rows.len(), 3);
+        let grid_meta_delta = make_grid_delta(&grid_meta);
+        let _: GridMeta = serde_json::from_str(&grid_meta_delta.to_str().unwrap()).unwrap();
 
-        assert_eq!(grid_meta.blocks[0].id, grid_block_meta.block_id);
+        let grid_block_meta_delta = make_block_meta_delta(&build_context.grid_block_meta);
+        let _: GridBlockMeta = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
     }
 }

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

@@ -1,6 +1,7 @@
 use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{internal_error, CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
+use bytes::Bytes;
 use flowy_grid_data_model::entities::{
     FieldChangeset, FieldMeta, FieldOrder, GridBlock, GridBlockChangeset, GridMeta, RepeatedFieldOrder,
 };
@@ -56,6 +57,14 @@ impl GridMetaPad {
         })
     }
 
+    pub fn contain_field(&self, field_id: &str) -> bool {
+        self.grid_meta
+            .fields
+            .iter()
+            .find(|field| &field.id == field_id)
+            .is_some()
+    }
+
     pub fn get_field_orders(&self) -> Vec<FieldOrder> {
         self.grid_meta
             .fields
@@ -180,13 +189,17 @@ impl GridMetaPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_bytes())
+        md5(&self.delta.to_delta_bytes())
     }
 
     pub fn delta_str(&self) -> String {
         self.delta.to_delta_str()
     }
 
+    pub fn delta_bytes(&self) -> Bytes {
+        self.delta.to_delta_bytes()
+    }
+
     pub fn fields(&self) -> &[FieldMeta] {
         &self.grid_meta.fields
     }
@@ -258,7 +271,7 @@ pub fn make_grid_delta(grid_meta: &GridMeta) -> GridMetaDelta {
 
 pub fn make_grid_revisions(user_id: &str, grid_meta: &GridMeta) -> RepeatedRevision {
     let delta = make_grid_delta(grid_meta);
-    let bytes = delta.to_bytes();
+    let bytes = delta.to_delta_bytes();
     let revision = Revision::initial_revision(user_id, &grid_meta.grid_id, bytes);
     revision.into()
 }

+ 2 - 2
shared-lib/flowy-folder-data-model/src/entities/view.rs

@@ -127,7 +127,7 @@ pub struct CreateViewPayload {
     pub plugin_type: i32,
 
     #[pb(index = 7)]
-    pub data: String,
+    pub data: Vec<u8>,
 }
 
 #[derive(Default, ProtoBuf, Debug, Clone)]
@@ -151,7 +151,7 @@ pub struct CreateViewParams {
     pub view_id: String,
 
     #[pb(index = 7)]
-    pub data: String,
+    pub data: Vec<u8>,
 
     #[pb(index = 8)]
     pub plugin_type: i32,

+ 38 - 38
shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs

@@ -794,7 +794,7 @@ pub struct CreateViewPayload {
     pub desc: ::std::string::String,
     pub data_type: ViewDataType,
     pub plugin_type: i32,
-    pub data: ::std::string::String,
+    pub data: ::std::vec::Vec<u8>,
     // message oneof groups
     pub one_of_thumbnail: ::std::option::Option<CreateViewPayload_oneof_one_of_thumbnail>,
     // special fields
@@ -975,10 +975,10 @@ impl CreateViewPayload {
         self.plugin_type = v;
     }
 
-    // string data = 7;
+    // bytes data = 7;
 
 
-    pub fn get_data(&self) -> &str {
+    pub fn get_data(&self) -> &[u8] {
         &self.data
     }
     pub fn clear_data(&mut self) {
@@ -986,19 +986,19 @@ impl CreateViewPayload {
     }
 
     // Param is passed by value, moved
-    pub fn set_data(&mut self, v: ::std::string::String) {
+    pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
         self.data = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_data(&mut self) -> &mut ::std::string::String {
+    pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
         &mut self.data
     }
 
     // Take field
-    pub fn take_data(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.data, ::std::string::String::new())
+    pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
+        ::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
     }
 }
 
@@ -1037,7 +1037,7 @@ impl ::protobuf::Message for CreateViewPayload {
                     self.plugin_type = tmp;
                 },
                 7 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
+                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -1067,7 +1067,7 @@ impl ::protobuf::Message for CreateViewPayload {
             my_size += ::protobuf::rt::value_size(6, self.plugin_type, ::protobuf::wire_format::WireTypeVarint);
         }
         if !self.data.is_empty() {
-            my_size += ::protobuf::rt::string_size(7, &self.data);
+            my_size += ::protobuf::rt::bytes_size(7, &self.data);
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
             match v {
@@ -1098,7 +1098,7 @@ impl ::protobuf::Message for CreateViewPayload {
             os.write_int32(6, self.plugin_type)?;
         }
         if !self.data.is_empty() {
-            os.write_string(7, &self.data)?;
+            os.write_bytes(7, &self.data)?;
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
             match v {
@@ -1175,7 +1175,7 @@ impl ::protobuf::Message for CreateViewPayload {
                 |m: &CreateViewPayload| { &m.plugin_type },
                 |m: &mut CreateViewPayload| { &mut m.plugin_type },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "data",
                 |m: &CreateViewPayload| { &m.data },
                 |m: &mut CreateViewPayload| { &mut m.data },
@@ -1228,7 +1228,7 @@ pub struct CreateViewParams {
     pub thumbnail: ::std::string::String,
     pub data_type: ViewDataType,
     pub view_id: ::std::string::String,
-    pub data: ::std::string::String,
+    pub data: ::std::vec::Vec<u8>,
     pub plugin_type: i32,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
@@ -1391,10 +1391,10 @@ impl CreateViewParams {
         ::std::mem::replace(&mut self.view_id, ::std::string::String::new())
     }
 
-    // string data = 7;
+    // bytes data = 7;
 
 
-    pub fn get_data(&self) -> &str {
+    pub fn get_data(&self) -> &[u8] {
         &self.data
     }
     pub fn clear_data(&mut self) {
@@ -1402,19 +1402,19 @@ impl CreateViewParams {
     }
 
     // Param is passed by value, moved
-    pub fn set_data(&mut self, v: ::std::string::String) {
+    pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
         self.data = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_data(&mut self) -> &mut ::std::string::String {
+    pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
         &mut self.data
     }
 
     // Take field
-    pub fn take_data(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.data, ::std::string::String::new())
+    pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
+        ::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
     }
 
     // int32 plugin_type = 8;
@@ -1461,7 +1461,7 @@ impl ::protobuf::Message for CreateViewParams {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_id)?;
                 },
                 7 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
+                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
                 },
                 8 => {
                     if wire_type != ::protobuf::wire_format::WireTypeVarint {
@@ -1501,7 +1501,7 @@ impl ::protobuf::Message for CreateViewParams {
             my_size += ::protobuf::rt::string_size(6, &self.view_id);
         }
         if !self.data.is_empty() {
-            my_size += ::protobuf::rt::string_size(7, &self.data);
+            my_size += ::protobuf::rt::bytes_size(7, &self.data);
         }
         if self.plugin_type != 0 {
             my_size += ::protobuf::rt::value_size(8, self.plugin_type, ::protobuf::wire_format::WireTypeVarint);
@@ -1531,7 +1531,7 @@ impl ::protobuf::Message for CreateViewParams {
             os.write_string(6, &self.view_id)?;
         }
         if !self.data.is_empty() {
-            os.write_string(7, &self.data)?;
+            os.write_bytes(7, &self.data)?;
         }
         if self.plugin_type != 0 {
             os.write_int32(8, self.plugin_type)?;
@@ -1604,7 +1604,7 @@ impl ::protobuf::Message for CreateViewParams {
                 |m: &CreateViewParams| { &m.view_id },
                 |m: &mut CreateViewParams| { &mut m.view_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "data",
                 |m: &CreateViewParams| { &m.data },
                 |m: &mut CreateViewParams| { &mut m.data },
@@ -2844,22 +2844,22 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x18\x03\x20\x01(\tR\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\0R\
     \tthumbnail\x12*\n\tdata_type\x18\x05\x20\x01(\x0e2\r.ViewDataTypeR\x08d\
     ataType\x12\x1f\n\x0bplugin_type\x18\x06\x20\x01(\x05R\npluginType\x12\
-    \x12\n\x04data\x18\x07\x20\x01(\tR\x04dataB\x12\n\x10one_of_thumbnail\"\
-    \xf4\x01\n\x10CreateViewParams\x12\x20\n\x0cbelong_to_id\x18\x01\x20\x01\
-    (\tR\nbelongToId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\
-    \x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1c\n\tthumbnail\x18\x04\x20\
-    \x01(\tR\tthumbnail\x12*\n\tdata_type\x18\x05\x20\x01(\x0e2\r.ViewDataTy\
-    peR\x08dataType\x12\x17\n\x07view_id\x18\x06\x20\x01(\tR\x06viewId\x12\
-    \x12\n\x04data\x18\x07\x20\x01(\tR\x04data\x12\x1f\n\x0bplugin_type\x18\
-    \x08\x20\x01(\x05R\npluginType\"\x1e\n\x06ViewId\x12\x14\n\x05value\x18\
-    \x01\x20\x01(\tR\x05value\"&\n\x0eRepeatedViewId\x12\x14\n\x05items\x18\
-    \x01\x20\x03(\tR\x05items\"\xaa\x01\n\x11UpdateViewPayload\x12\x17\n\x07\
-    view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\x01(\
-    \tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\x12\
-    \x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of_nam\
-    eB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail\"\xa9\x01\n\x10UpdateVi\
-    ewParams\x12\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\
-    \x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\
+    \x12\n\x04data\x18\x07\x20\x01(\x0cR\x04dataB\x12\n\x10one_of_thumbnail\
+    \"\xf4\x01\n\x10CreateViewParams\x12\x20\n\x0cbelong_to_id\x18\x01\x20\
+    \x01(\tR\nbelongToId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\
+    \x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1c\n\tthumbnail\x18\x04\
+    \x20\x01(\tR\tthumbnail\x12*\n\tdata_type\x18\x05\x20\x01(\x0e2\r.ViewDa\
+    taTypeR\x08dataType\x12\x17\n\x07view_id\x18\x06\x20\x01(\tR\x06viewId\
+    \x12\x12\n\x04data\x18\x07\x20\x01(\x0cR\x04data\x12\x1f\n\x0bplugin_typ\
+    e\x18\x08\x20\x01(\x05R\npluginType\"\x1e\n\x06ViewId\x12\x14\n\x05value\
+    \x18\x01\x20\x01(\tR\x05value\"&\n\x0eRepeatedViewId\x12\x14\n\x05items\
+    \x18\x01\x20\x03(\tR\x05items\"\xaa\x01\n\x11UpdateViewPayload\x12\x17\n\
+    \x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\
+    \x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\
+    \x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of\
+    _nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail\"\xa9\x01\n\x10Upda\
+    teViewParams\x12\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\
+    \n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\
     \x01(\tH\x01R\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthu\
     mbnailB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnai\
     l*'\n\x0cViewDataType\x12\r\n\tTextBlock\x10\0\x12\x08\n\x04Grid\x10\x01\

+ 2 - 2
shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto

@@ -24,7 +24,7 @@ message CreateViewPayload {
     oneof one_of_thumbnail { string thumbnail = 4; };
     ViewDataType data_type = 5;
     int32 plugin_type = 6;
-    string data = 7;
+    bytes data = 7;
 }
 message CreateViewParams {
     string belong_to_id = 1;
@@ -33,7 +33,7 @@ message CreateViewParams {
     string thumbnail = 4;
     ViewDataType data_type = 5;
     string view_id = 6;
-    string data = 7;
+    bytes data = 7;
     int32 plugin_type = 8;
 }
 message ViewId {

+ 33 - 2
shared-lib/flowy-grid-data-model/src/entities/meta.rs

@@ -1,7 +1,8 @@
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
-use strum_macros::{Display, EnumIter, EnumString};
+use strum::{EnumCount, IntoEnumIterator};
+use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
 
 pub const DEFAULT_ROW_HEIGHT: i32 = 36;
 pub const DEFAULT_FIELD_WIDTH: i32 = 150;
@@ -139,7 +140,9 @@ pub struct FieldChangeset {
     pub type_options: Option<String>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumString, EnumIter, Display, Serialize, Deserialize)]
+#[derive(
+    Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumCountMacro, EnumString, EnumIter, Display, Serialize, Deserialize,
+)]
 pub enum FieldType {
     RichText = 0,
     Number = 1,
@@ -312,3 +315,31 @@ impl std::convert::From<CellMetaChangeset> for RowMetaChangeset {
         }
     }
 }
+
+#[derive(Clone, ProtoBuf)]
+pub struct BuildGridContext {
+    #[pb(index = 1)]
+    pub field_metas: Vec<FieldMeta>,
+
+    #[pb(index = 2)]
+    pub grid_block: GridBlock,
+
+    #[pb(index = 3)]
+    pub grid_block_meta: GridBlockMeta,
+}
+
+impl std::default::Default for BuildGridContext {
+    fn default() -> Self {
+        let grid_block = GridBlock::new();
+        let grid_block_meta = GridBlockMeta {
+            block_id: grid_block.id.clone(),
+            rows: vec![],
+        };
+
+        Self {
+            field_metas: vec![],
+            grid_block,
+            grid_block_meta,
+        }
+    }
+}

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

@@ -3073,6 +3073,286 @@ impl ::protobuf::reflect::ProtobufValue for CellMetaChangeset {
     }
 }
 
+#[derive(PartialEq,Clone,Default)]
+pub struct BuildGridContext {
+    // message fields
+    pub field_metas: ::protobuf::RepeatedField<FieldMeta>,
+    pub grid_block: ::protobuf::SingularPtrField<GridBlock>,
+    pub grid_block_meta: ::protobuf::SingularPtrField<GridBlockMeta>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a BuildGridContext {
+    fn default() -> &'a BuildGridContext {
+        <BuildGridContext as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl BuildGridContext {
+    pub fn new() -> BuildGridContext {
+        ::std::default::Default::default()
+    }
+
+    // repeated .FieldMeta field_metas = 1;
+
+
+    pub fn get_field_metas(&self) -> &[FieldMeta] {
+        &self.field_metas
+    }
+    pub fn clear_field_metas(&mut self) {
+        self.field_metas.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_metas(&mut self, v: ::protobuf::RepeatedField<FieldMeta>) {
+        self.field_metas = v;
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_field_metas(&mut self) -> &mut ::protobuf::RepeatedField<FieldMeta> {
+        &mut self.field_metas
+    }
+
+    // Take field
+    pub fn take_field_metas(&mut self) -> ::protobuf::RepeatedField<FieldMeta> {
+        ::std::mem::replace(&mut self.field_metas, ::protobuf::RepeatedField::new())
+    }
+
+    // .GridBlock grid_block = 2;
+
+
+    pub fn get_grid_block(&self) -> &GridBlock {
+        self.grid_block.as_ref().unwrap_or_else(|| <GridBlock as ::protobuf::Message>::default_instance())
+    }
+    pub fn clear_grid_block(&mut self) {
+        self.grid_block.clear();
+    }
+
+    pub fn has_grid_block(&self) -> bool {
+        self.grid_block.is_some()
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_block(&mut self, v: GridBlock) {
+        self.grid_block = ::protobuf::SingularPtrField::some(v);
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_block(&mut self) -> &mut GridBlock {
+        if self.grid_block.is_none() {
+            self.grid_block.set_default();
+        }
+        self.grid_block.as_mut().unwrap()
+    }
+
+    // Take field
+    pub fn take_grid_block(&mut self) -> GridBlock {
+        self.grid_block.take().unwrap_or_else(|| GridBlock::new())
+    }
+
+    // .GridBlockMeta grid_block_meta = 3;
+
+
+    pub fn get_grid_block_meta(&self) -> &GridBlockMeta {
+        self.grid_block_meta.as_ref().unwrap_or_else(|| <GridBlockMeta as ::protobuf::Message>::default_instance())
+    }
+    pub fn clear_grid_block_meta(&mut self) {
+        self.grid_block_meta.clear();
+    }
+
+    pub fn has_grid_block_meta(&self) -> bool {
+        self.grid_block_meta.is_some()
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_block_meta(&mut self, v: GridBlockMeta) {
+        self.grid_block_meta = ::protobuf::SingularPtrField::some(v);
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_block_meta(&mut self) -> &mut GridBlockMeta {
+        if self.grid_block_meta.is_none() {
+            self.grid_block_meta.set_default();
+        }
+        self.grid_block_meta.as_mut().unwrap()
+    }
+
+    // Take field
+    pub fn take_grid_block_meta(&mut self) -> GridBlockMeta {
+        self.grid_block_meta.take().unwrap_or_else(|| GridBlockMeta::new())
+    }
+}
+
+impl ::protobuf::Message for BuildGridContext {
+    fn is_initialized(&self) -> bool {
+        for v in &self.field_metas {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        for v in &self.grid_block {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        for v in &self.grid_block_meta {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.field_metas)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.grid_block)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.grid_block_meta)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        for value in &self.field_metas {
+            let len = value.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        };
+        if let Some(ref v) = self.grid_block.as_ref() {
+            let len = v.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        }
+        if let Some(ref v) = self.grid_block_meta.as_ref() {
+            let len = v.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        for v in &self.field_metas {
+            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        };
+        if let Some(ref v) = self.grid_block.as_ref() {
+            os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        }
+        if let Some(ref v) = self.grid_block_meta.as_ref() {
+            os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        }
+        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() -> BuildGridContext {
+        BuildGridContext::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<FieldMeta>>(
+                "field_metas",
+                |m: &BuildGridContext| { &m.field_metas },
+                |m: &mut BuildGridContext| { &mut m.field_metas },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlock>>(
+                "grid_block",
+                |m: &BuildGridContext| { &m.grid_block },
+                |m: &mut BuildGridContext| { &mut m.grid_block },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<GridBlockMeta>>(
+                "grid_block_meta",
+                |m: &BuildGridContext| { &m.grid_block_meta },
+                |m: &mut BuildGridContext| { &mut m.grid_block_meta },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<BuildGridContext>(
+                "BuildGridContext",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static BuildGridContext {
+        static instance: ::protobuf::rt::LazyV2<BuildGridContext> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(BuildGridContext::new)
+    }
+}
+
+impl ::protobuf::Clear for BuildGridContext {
+    fn clear(&mut self) {
+        self.field_metas.clear();
+        self.grid_block.clear();
+        self.grid_block_meta.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for BuildGridContext {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for BuildGridContext {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
 pub enum FieldType {
     RichText = 0,
@@ -3178,10 +3458,13 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     ata\x18\x02\x20\x01(\tR\x04data\"j\n\x11CellMetaChangeset\x12\x15\n\x06r\
     ow_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x02\x20\x01\
     (\tR\x07fieldId\x12\x14\n\x04data\x18\x03\x20\x01(\tH\0R\x04dataB\r\n\
-    \x0bone_of_data*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Nu\
-    mber\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\x06\
-    proto3\
+    \x0bone_of_data\"\xa2\x01\n\x10BuildGridContext\x12+\n\x0bfield_metas\
+    \x18\x01\x20\x03(\x0b2\n.FieldMetaR\nfieldMetas\x12)\n\ngrid_block\x18\
+    \x02\x20\x01(\x0b2\n.GridBlockR\tgridBlock\x126\n\x0fgrid_block_meta\x18\
+    \x03\x20\x01(\x0b2\x0e.GridBlockMetaR\rgridBlockMeta*d\n\tFieldType\x12\
+    \x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\
+    \x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\
+    \x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -60,6 +60,11 @@ message CellMetaChangeset {
     string field_id = 2;
     oneof one_of_data { string data = 3; };
 }
+message BuildGridContext {
+    repeated FieldMeta field_metas = 1;
+    GridBlock grid_block = 2;
+    GridBlockMeta grid_block_meta = 3;
+}
 enum FieldType {
     RichText = 0;
     Number = 1;

+ 1 - 1
shared-lib/lib-ot/src/core/delta/delta.rs

@@ -529,7 +529,7 @@ where
         self.apply("")
     }
 
-    pub fn to_bytes(&self) -> Bytes {
+    pub fn to_delta_bytes(&self) -> Bytes {
         let json = self.to_delta_str();
         Bytes::from(json.into_bytes())
     }