Bläddra i källkod

chore: update field type option data using deserializer

appflowy 3 år sedan
förälder
incheckning
80f82fa5b1

+ 7 - 5
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -10,12 +10,13 @@ class FieldService {
 
   FieldService({required this.gridId});
 
-  Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(FieldType fieldType) {
-    final payload = GetEditFieldContextParams.create()
+  Future<Either<EditFieldContext, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
+    final payload = EditFieldPayload.create()
       ..gridId = gridId
+      ..fieldId = fieldId
       ..fieldType = fieldType;
 
-    return GridEventGetEditFieldContext(payload).send();
+    return GridEventSwitchToField(payload).send();
   }
 
   Future<Either<Unit, FlowyError>> updateField({
@@ -58,6 +59,7 @@ class FieldService {
     return GridEventUpdateField(payload).send();
   }
 
+  // Create the field if it does not exist. Otherwise, update the field.
   Future<Either<Unit, FlowyError>> createField({
     required Field field,
     List<int>? typeOptionData,
@@ -121,7 +123,7 @@ class NewFieldContextLoader extends FieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> load() {
-    final payload = GetEditFieldContextParams.create()
+    final payload = GetEditFieldContextPayload.create()
       ..gridId = gridId
       ..fieldType = FieldType.RichText;
 
@@ -136,7 +138,7 @@ class FieldContextLoaderAdaptor extends FieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> load() {
-    final payload = GetEditFieldContextParams.create()
+    final payload = GetEditFieldContextPayload.create()
       ..gridId = data.gridId
       ..fieldId = data.field.id
       ..fieldType = data.field.fieldType;

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

@@ -16,7 +16,7 @@ class FieldSwitchBloc extends Bloc<FieldSwitchEvent, FieldSwitchState> {
         await event.map(
           toFieldType: (_ToFieldType value) async {
             final fieldService = FieldService(gridId: state.gridId);
-            final result = await fieldService.getEditFieldContext(value.fieldType);
+            final result = await fieldService.switchToField(state.field.id, value.fieldType);
             result.fold(
               (newEditContext) {
                 final typeOptionData = Uint8List.fromList(newEditContext.typeOptionData);

+ 18 - 1
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart

@@ -103,6 +103,23 @@ class GridEventDeleteField {
     }
 }
 
+class GridEventSwitchToField {
+     EditFieldPayload request;
+     GridEventSwitchToField(this.request);
+
+    Future<Either<EditFieldContext, FlowyError>> send() {
+    final request = FFIRequest.create()
+          ..event = GridEvent.SwitchToField.toString()
+          ..payload = requestToBytes(this.request);
+
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (okBytes) => left(EditFieldContext.fromBuffer(okBytes)),
+           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+        ));
+    }
+}
+
 class GridEventDuplicateField {
      FieldIdentifierPayload request;
      GridEventDuplicateField(this.request);
@@ -121,7 +138,7 @@ class GridEventDuplicateField {
 }
 
 class GridEventGetEditFieldContext {
-     GetEditFieldContextParams request;
+     GetEditFieldContextPayload request;
      GridEventGetEditFieldContext(this.request);
 
     Future<Either<EditFieldContext, FlowyError>> send() {

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

@@ -374,17 +374,17 @@ class FieldOrder extends $pb.GeneratedMessage {
   void clearFieldId() => clearField(1);
 }
 
-enum GetEditFieldContextParams_OneOfFieldId {
+enum GetEditFieldContextPayload_OneOfFieldId {
   fieldId, 
   notSet
 }
 
-class GetEditFieldContextParams extends $pb.GeneratedMessage {
-  static const $core.Map<$core.int, GetEditFieldContextParams_OneOfFieldId> _GetEditFieldContextParams_OneOfFieldIdByTag = {
-    2 : GetEditFieldContextParams_OneOfFieldId.fieldId,
-    0 : GetEditFieldContextParams_OneOfFieldId.notSet
+class GetEditFieldContextPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, GetEditFieldContextPayload_OneOfFieldId> _GetEditFieldContextPayload_OneOfFieldIdByTag = {
+    2 : GetEditFieldContextPayload_OneOfFieldId.fieldId,
+    0 : GetEditFieldContextPayload_OneOfFieldId.notSet
   };
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetEditFieldContextParams', createEmptyInstance: create)
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetEditFieldContextPayload', createEmptyInstance: create)
     ..oo(0, [2])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
@@ -392,8 +392,8 @@ class GetEditFieldContextParams extends $pb.GeneratedMessage {
     ..hasRequiredFields = false
   ;
 
-  GetEditFieldContextParams._() : super();
-  factory GetEditFieldContextParams({
+  GetEditFieldContextPayload._() : super();
+  factory GetEditFieldContextPayload({
     $core.String? gridId,
     $core.String? fieldId,
     $0.FieldType? fieldType,
@@ -410,28 +410,28 @@ class GetEditFieldContextParams extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory GetEditFieldContextParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory GetEditFieldContextParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory GetEditFieldContextPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetEditFieldContextPayload.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')
-  GetEditFieldContextParams clone() => GetEditFieldContextParams()..mergeFromMessage(this);
+  GetEditFieldContextPayload clone() => GetEditFieldContextPayload()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  GetEditFieldContextParams copyWith(void Function(GetEditFieldContextParams) updates) => super.copyWith((message) => updates(message as GetEditFieldContextParams)) as GetEditFieldContextParams; // ignore: deprecated_member_use
+  GetEditFieldContextPayload copyWith(void Function(GetEditFieldContextPayload) updates) => super.copyWith((message) => updates(message as GetEditFieldContextPayload)) as GetEditFieldContextPayload; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static GetEditFieldContextParams create() => GetEditFieldContextParams._();
-  GetEditFieldContextParams createEmptyInstance() => create();
-  static $pb.PbList<GetEditFieldContextParams> createRepeated() => $pb.PbList<GetEditFieldContextParams>();
+  static GetEditFieldContextPayload create() => GetEditFieldContextPayload._();
+  GetEditFieldContextPayload createEmptyInstance() => create();
+  static $pb.PbList<GetEditFieldContextPayload> createRepeated() => $pb.PbList<GetEditFieldContextPayload>();
   @$core.pragma('dart2js:noInline')
-  static GetEditFieldContextParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetEditFieldContextParams>(create);
-  static GetEditFieldContextParams? _defaultInstance;
+  static GetEditFieldContextPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetEditFieldContextPayload>(create);
+  static GetEditFieldContextPayload? _defaultInstance;
 
-  GetEditFieldContextParams_OneOfFieldId whichOneOfFieldId() => _GetEditFieldContextParams_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  GetEditFieldContextPayload_OneOfFieldId whichOneOfFieldId() => _GetEditFieldContextPayload_OneOfFieldIdByTag[$_whichOneof(0)]!;
   void clearOneOfFieldId() => clearField($_whichOneof(0));
 
   @$pb.TagNumber(1)
@@ -462,6 +462,81 @@ class GetEditFieldContextParams extends $pb.GeneratedMessage {
   void clearFieldType() => clearField(3);
 }
 
+class EditFieldPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..e<$0.FieldType>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: $0.FieldType.RichText, valueOf: $0.FieldType.valueOf, enumValues: $0.FieldType.values)
+    ..hasRequiredFields = false
+  ;
+
+  EditFieldPayload._() : super();
+  factory EditFieldPayload({
+    $core.String? gridId,
+    $core.String? fieldId,
+    $0.FieldType? fieldType,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (fieldType != null) {
+      _result.fieldType = fieldType;
+    }
+    return _result;
+  }
+  factory EditFieldPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory EditFieldPayload.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')
+  EditFieldPayload clone() => EditFieldPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  EditFieldPayload copyWith(void Function(EditFieldPayload) updates) => super.copyWith((message) => updates(message as EditFieldPayload)) as EditFieldPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static EditFieldPayload create() => EditFieldPayload._();
+  EditFieldPayload createEmptyInstance() => create();
+  static $pb.PbList<EditFieldPayload> createRepeated() => $pb.PbList<EditFieldPayload>();
+  @$core.pragma('dart2js:noInline')
+  static EditFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EditFieldPayload>(create);
+  static EditFieldPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get gridId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set gridId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGridId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGridId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get fieldId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set fieldId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasFieldId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearFieldId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $0.FieldType get fieldType => $_getN(2);
+  @$pb.TagNumber(3)
+  set fieldType($0.FieldType v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasFieldType() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearFieldType() => clearField(3);
+}
+
 class EditFieldContext extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldContext', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')

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

@@ -68,9 +68,9 @@ const FieldOrder$json = const {
 
 /// Descriptor for `FieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List fieldOrderDescriptor = $convert.base64Decode('CgpGaWVsZE9yZGVyEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElk');
-@$core.Deprecated('Use getEditFieldContextParamsDescriptor instead')
-const GetEditFieldContextParams$json = const {
-  '1': 'GetEditFieldContextParams',
+@$core.Deprecated('Use getEditFieldContextPayloadDescriptor instead')
+const GetEditFieldContextPayload$json = const {
+  '1': 'GetEditFieldContextPayload',
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
     const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
@@ -81,8 +81,20 @@ const GetEditFieldContextParams$json = const {
   ],
 };
 
-/// Descriptor for `GetEditFieldContextParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List getEditFieldContextParamsDescriptor = $convert.base64Decode('ChlHZXRFZGl0RmllbGRDb250ZXh0UGFyYW1zEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIbCghmaWVsZF9pZBgCIAEoCUgAUgdmaWVsZElkEikKCmZpZWxkX3R5cGUYAyABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZUIRCg9vbmVfb2ZfZmllbGRfaWQ=');
+/// Descriptor for `GetEditFieldContextPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getEditFieldContextPayloadDescriptor = $convert.base64Decode('ChpHZXRFZGl0RmllbGRDb250ZXh0UGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSGwoIZmllbGRfaWQYAiABKAlIAFIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGVCEQoPb25lX29mX2ZpZWxkX2lk');
+@$core.Deprecated('Use editFieldPayloadDescriptor instead')
+const EditFieldPayload$json = const {
+  '1': 'EditFieldPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '10': 'fieldId'},
+    const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+  ],
+};
+
+/// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGU=');
 @$core.Deprecated('Use editFieldContextDescriptor instead')
 const EditFieldContext$json = const {
   '1': 'EditFieldContext',

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

@@ -16,6 +16,7 @@ class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent UpdateField = GridEvent._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateField');
   static const GridEvent CreateField = GridEvent._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateField');
   static const GridEvent DeleteField = GridEvent._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteField');
+  static const GridEvent SwitchToField = GridEvent._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SwitchToField');
   static const GridEvent DuplicateField = GridEvent._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField');
   static const GridEvent GetEditFieldContext = GridEvent._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetEditFieldContext');
   static const GridEvent CreateSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateSelectOption');
@@ -30,6 +31,7 @@ class GridEvent extends $pb.ProtobufEnum {
     UpdateField,
     CreateField,
     DeleteField,
+    SwitchToField,
     DuplicateField,
     GetEditFieldContext,
     CreateSelectOption,

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

@@ -18,6 +18,7 @@ const GridEvent$json = const {
     const {'1': 'UpdateField', '2': 11},
     const {'1': 'CreateField', '2': 12},
     const {'1': 'DeleteField', '2': 13},
+    const {'1': 'SwitchToField', '2': 14},
     const {'1': 'DuplicateField', '2': 15},
     const {'1': 'GetEditFieldContext', '2': 16},
     const {'1': 'CreateSelectOption', '2': 30},
@@ -28,4 +29,4 @@ const GridEvent$json = const {
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEgoORHVwbGljYXRlRmllbGQQDxIXChNHZXRFZGl0RmllbGRDb250ZXh0EBASFgoSQ3JlYXRlU2VsZWN0T3B0aW9uEB4SDQoJQ3JlYXRlUm93EDISCgoGR2V0Um93EDMSDgoKVXBkYXRlQ2VsbBBG');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhYKEkNyZWF0ZVNlbGVjdE9wdGlvbhAeEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg4KClVwZGF0ZUNlbGwQRg==');

+ 15 - 4
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -78,6 +78,17 @@ pub(crate) async fn delete_field_handler(
     Ok(())
 }
 
+#[tracing::instrument(level = "debug", skip(data, manager), err)]
+pub(crate) async fn switch_to_field_handler(
+    data: Data<EditFieldPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> DataResult<EditFieldContext, FlowyError> {
+    let params: EditFieldParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    let edit_context = editor.switch_to_field_type(&params.field_id, params.field_type).await?;
+    data_result(edit_context)
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn duplicate_field_handler(
     data: Data<FieldIdentifierPayload>,
@@ -98,11 +109,11 @@ pub(crate) async fn create_select_option_handler(
 }
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
-pub(crate) async fn edit_edit_field_context_handler(
-    data: Data<GetEditFieldContextParams>,
+pub(crate) async fn get_field_context_handler(
+    data: Data<GetEditFieldContextPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<EditFieldContext, FlowyError> {
-    let params: GetEditFieldContextParams = data.into_inner();
+    let params = data.into_inner();
     let editor = manager.get_grid_editor(&params.grid_id)?;
 
     let mut field_meta = get_or_create_field_meta(&params, editor).await?;
@@ -117,7 +128,7 @@ pub(crate) async fn edit_edit_field_context_handler(
 }
 
 async fn get_or_create_field_meta(
-    params: &GetEditFieldContextParams,
+    params: &GetEditFieldContextPayload,
     editor: Arc<ClientGridEditor>,
 ) -> FlowyResult<FieldMeta> {
     if params.field_id.is_some() {

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

@@ -14,8 +14,9 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::UpdateField, update_field_handler)
         .event(GridEvent::CreateField, create_field_handler)
         .event(GridEvent::DeleteField, delete_field_handler)
+        .event(GridEvent::SwitchToField, switch_to_field_handler)
         .event(GridEvent::DuplicateField, duplicate_field_handler)
-        .event(GridEvent::GetEditFieldContext, edit_edit_field_context_handler)
+        .event(GridEvent::GetEditFieldContext, get_field_context_handler)
         .event(GridEvent::CreateSelectOption, create_select_option_handler)
         .event(GridEvent::CreateRow, create_row_handler)
         .event(GridEvent::GetRow, get_row_handler)
@@ -45,10 +46,13 @@ pub enum GridEvent {
     #[event(input = "FieldIdentifierPayload")]
     DeleteField = 13,
 
+    #[event(input = "EditFieldPayload", output = "EditFieldContext")]
+    SwitchToField = 14,
+
     #[event(input = "FieldIdentifierPayload")]
     DuplicateField = 15,
 
-    #[event(input = "GetEditFieldContextParams", output = "EditFieldContext")]
+    #[event(input = "GetEditFieldContextPayload", output = "EditFieldContext")]
     GetEditFieldContext = 16,
 
     #[event(input = "CreateSelectOptionPayload", output = "SelectOption")]

+ 8 - 5
frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs

@@ -31,6 +31,7 @@ pub enum GridEvent {
     UpdateField = 11,
     CreateField = 12,
     DeleteField = 13,
+    SwitchToField = 14,
     DuplicateField = 15,
     GetEditFieldContext = 16,
     CreateSelectOption = 30,
@@ -52,6 +53,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             11 => ::std::option::Option::Some(GridEvent::UpdateField),
             12 => ::std::option::Option::Some(GridEvent::CreateField),
             13 => ::std::option::Option::Some(GridEvent::DeleteField),
+            14 => ::std::option::Option::Some(GridEvent::SwitchToField),
             15 => ::std::option::Option::Some(GridEvent::DuplicateField),
             16 => ::std::option::Option::Some(GridEvent::GetEditFieldContext),
             30 => ::std::option::Option::Some(GridEvent::CreateSelectOption),
@@ -70,6 +72,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::UpdateField,
             GridEvent::CreateField,
             GridEvent::DeleteField,
+            GridEvent::SwitchToField,
             GridEvent::DuplicateField,
             GridEvent::GetEditFieldContext,
             GridEvent::CreateSelectOption,
@@ -104,13 +107,13 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xe1\x01\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
+    \n\x0fevent_map.proto*\xf4\x01\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
     \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
     \x0bUpdateField\x10\x0b\x12\x0f\n\x0bCreateField\x10\x0c\x12\x0f\n\x0bDe\
-    leteField\x10\r\x12\x12\n\x0eDuplicateField\x10\x0f\x12\x17\n\x13GetEdit\
-    FieldContext\x10\x10\x12\x16\n\x12CreateSelectOption\x10\x1e\x12\r\n\tCr\
-    eateRow\x102\x12\n\n\x06GetRow\x103\x12\x0e\n\nUpdateCell\x10Fb\x06proto\
-    3\
+    leteField\x10\r\x12\x11\n\rSwitchToField\x10\x0e\x12\x12\n\x0eDuplicateF\
+    ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x16\n\x12Creat\
+    eSelectOption\x10\x1e\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\
+    \x0e\n\nUpdateCell\x10Fb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -7,6 +7,7 @@ enum GridEvent {
     UpdateField = 11;
     CreateField = 12;
     DeleteField = 13;
+    SwitchToField = 14;
     DuplicateField = 15;
     GetEditFieldContext = 16;
     CreateSelectOption = 30;

+ 26 - 15
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -7,8 +7,9 @@ use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
 use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
-use flowy_sync::client_grid::{GridChangeset, GridMetaPad};
+use flowy_sync::client_grid::{GridChangeset, GridMetaPad, TypeOptionDataDeserializer};
 use flowy_sync::entities::revision::Revision;
+use flowy_sync::errors::CollaborateResult;
 use flowy_sync::util::make_delta_from_revisions;
 use lib_infra::future::FutureResult;
 use lib_ot::core::PlainTextAttributes;
@@ -59,6 +60,7 @@ impl ClientGridEditor {
         let _ = self
             .modify(|grid| {
                 if grid.contain_field(&field.id) {
+                    let deserializer = TypeOptionChangesetDeserializer(field.field_type.clone());
                     let changeset = FieldChangesetParams {
                         field_id: field.id,
                         grid_id,
@@ -70,8 +72,7 @@ impl ClientGridEditor {
                         width: Some(field.width),
                         type_option_data: Some(type_option_data),
                     };
-
-                    Ok(grid.update_field(changeset)?)
+                    Ok(grid.update_field(changeset, deserializer)?)
                 } else {
                     let type_option_json = type_option_json_str_from_bytes(type_option_data, &field.field_type);
                     let field_meta = FieldMeta {
@@ -103,19 +104,12 @@ impl ClientGridEditor {
     }
 
     pub async fn update_field(&self, mut params: FieldChangesetParams) -> FlowyResult<()> {
-        if let Some(type_option_data) = params.type_option_data {
-            match self.pad.read().await.get_field(&params.field_id) {
-                None => return Err(ErrorCode::FieldDoesNotExist.into()),
-                Some(field_meta) => {
-                    // The type_option_data is serialized by protobuf. But the type_option_data should be
-                    // serialized by utf-8 encoding. So we must transform the data here.
-                    let type_option_json = type_option_json_str_from_bytes(type_option_data, &field_meta.field_type);
-                    params.type_option_data = Some(type_option_json.as_bytes().to_vec());
-                }
-            }
-        }
+        let deserializer = match self.pad.read().await.get_field(&params.field_id) {
+            None => return Err(ErrorCode::FieldDoesNotExist.into()),
+            Some(field_meta) => TypeOptionChangesetDeserializer(field_meta.field_type.clone()),
+        };
 
-        let _ = self.modify(|grid| Ok(grid.update_field(params)?)).await?;
+        let _ = self.modify(|grid| Ok(grid.update_field(params, deserializer)?)).await?;
         let _ = self.notify_did_update_fields().await?;
         Ok(())
     }
@@ -126,6 +120,12 @@ impl ClientGridEditor {
         Ok(())
     }
 
+    pub async fn switch_to_field_type(&self, field_id: &str, field_type: FieldType) -> FlowyResult<EditFieldContext> {
+        let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?;
+        let _ = self.notify_did_update_fields().await?;
+        todo!()
+    }
+
     pub async fn duplicate_field(&self, field_id: &str) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.duplicate_field(field_id)?)).await?;
         let _ = self.notify_did_update_fields().await?;
@@ -402,3 +402,14 @@ impl RevisionCompactor for GridRevisionCompactor {
         Ok(delta.to_delta_bytes())
     }
 }
+
+struct TypeOptionChangesetDeserializer(FieldType);
+impl TypeOptionDataDeserializer for TypeOptionChangesetDeserializer {
+    fn deserialize(&self, type_option_data: Vec<u8>) -> CollaborateResult<String> {
+        // The type_option_data is serialized by protobuf. But the type_option_data should be
+        // serialized by utf-8. So we must transform the data here.
+
+        let type_option_json = type_option_json_str_from_bytes(type_option_data, &self.0);
+        Ok(type_option_json)
+    }
+}

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

@@ -101,7 +101,7 @@ impl std::convert::From<&FieldMeta> for FieldOrder {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct GetEditFieldContextParams {
+pub struct GetEditFieldContextPayload {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -112,6 +112,38 @@ pub struct GetEditFieldContextParams {
     pub field_type: FieldType,
 }
 
+#[derive(Debug, Default, ProtoBuf)]
+pub struct EditFieldPayload {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub field_type: FieldType,
+}
+
+pub struct EditFieldParams {
+    pub grid_id: String,
+    pub field_id: String,
+    pub field_type: FieldType,
+}
+
+impl TryInto<EditFieldParams> for EditFieldPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<EditFieldParams, Self::Error> {
+        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        Ok(EditFieldParams {
+            grid_id: grid_id.0,
+            field_id: field_id.0,
+            field_type: self.field_type,
+        })
+    }
+}
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct EditFieldContext {
     #[pb(index = 1)]

+ 313 - 78
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -1221,30 +1221,30 @@ impl ::protobuf::reflect::ProtobufValue for FieldOrder {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct GetEditFieldContextParams {
+pub struct GetEditFieldContextPayload {
     // message fields
     pub grid_id: ::std::string::String,
     pub field_type: super::meta::FieldType,
     // message oneof groups
-    pub one_of_field_id: ::std::option::Option<GetEditFieldContextParams_oneof_one_of_field_id>,
+    pub one_of_field_id: ::std::option::Option<GetEditFieldContextPayload_oneof_one_of_field_id>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a GetEditFieldContextParams {
-    fn default() -> &'a GetEditFieldContextParams {
-        <GetEditFieldContextParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a GetEditFieldContextPayload {
+    fn default() -> &'a GetEditFieldContextPayload {
+        <GetEditFieldContextPayload as ::protobuf::Message>::default_instance()
     }
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum GetEditFieldContextParams_oneof_one_of_field_id {
+pub enum GetEditFieldContextPayload_oneof_one_of_field_id {
     field_id(::std::string::String),
 }
 
-impl GetEditFieldContextParams {
-    pub fn new() -> GetEditFieldContextParams {
+impl GetEditFieldContextPayload {
+    pub fn new() -> GetEditFieldContextPayload {
         ::std::default::Default::default()
     }
 
@@ -1279,7 +1279,7 @@ impl GetEditFieldContextParams {
 
     pub fn get_field_id(&self) -> &str {
         match self.one_of_field_id {
-            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v)) => v,
+            ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(ref v)) => v,
             _ => "",
         }
     }
@@ -1289,24 +1289,24 @@ impl GetEditFieldContextParams {
 
     pub fn has_field_id(&self) -> bool {
         match self.one_of_field_id {
-            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(..)) => true,
+            ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
     pub fn set_field_id(&mut self, v: ::std::string::String) {
-        self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(v))
+        self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(v))
     }
 
     // Mutable pointer to the field.
     pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(_)) = self.one_of_field_id {
+        if let ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(_)) = self.one_of_field_id {
         } else {
-            self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(::std::string::String::new()));
+            self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(::std::string::String::new()));
         }
         match self.one_of_field_id {
-            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref mut v)) => v,
+            ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(ref mut v)) => v,
             _ => panic!(),
         }
     }
@@ -1315,7 +1315,7 @@ impl GetEditFieldContextParams {
     pub fn take_field_id(&mut self) -> ::std::string::String {
         if self.has_field_id() {
             match self.one_of_field_id.take() {
-                ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(v)) => v,
+                ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -1339,7 +1339,7 @@ impl GetEditFieldContextParams {
     }
 }
 
-impl ::protobuf::Message for GetEditFieldContextParams {
+impl ::protobuf::Message for GetEditFieldContextPayload {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -1355,7 +1355,7 @@ impl ::protobuf::Message for GetEditFieldContextParams {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(is.read_string()?));
+                    self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextPayload_oneof_one_of_field_id::field_id(is.read_string()?));
                 },
                 3 => {
                     ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 3, &mut self.unknown_fields)?
@@ -1380,7 +1380,7 @@ impl ::protobuf::Message for GetEditFieldContextParams {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
             match v {
-                &GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v) => {
+                &GetEditFieldContextPayload_oneof_one_of_field_id::field_id(ref v) => {
                     my_size += ::protobuf::rt::string_size(2, &v);
                 },
             };
@@ -1399,7 +1399,7 @@ impl ::protobuf::Message for GetEditFieldContextParams {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
             match v {
-                &GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v) => {
+                &GetEditFieldContextPayload_oneof_one_of_field_id::field_id(ref v) => {
                     os.write_string(2, v)?;
                 },
             };
@@ -1434,8 +1434,8 @@ impl ::protobuf::Message for GetEditFieldContextParams {
         Self::descriptor_static()
     }
 
-    fn new() -> GetEditFieldContextParams {
-        GetEditFieldContextParams::new()
+    fn new() -> GetEditFieldContextPayload {
+        GetEditFieldContextPayload::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -1444,34 +1444,34 @@ impl ::protobuf::Message for GetEditFieldContextParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "grid_id",
-                |m: &GetEditFieldContextParams| { &m.grid_id },
-                |m: &mut GetEditFieldContextParams| { &mut m.grid_id },
+                |m: &GetEditFieldContextPayload| { &m.grid_id },
+                |m: &mut GetEditFieldContextPayload| { &mut m.grid_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "field_id",
-                GetEditFieldContextParams::has_field_id,
-                GetEditFieldContextParams::get_field_id,
+                GetEditFieldContextPayload::has_field_id,
+                GetEditFieldContextPayload::get_field_id,
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::meta::FieldType>>(
                 "field_type",
-                |m: &GetEditFieldContextParams| { &m.field_type },
-                |m: &mut GetEditFieldContextParams| { &mut m.field_type },
+                |m: &GetEditFieldContextPayload| { &m.field_type },
+                |m: &mut GetEditFieldContextPayload| { &mut m.field_type },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GetEditFieldContextParams>(
-                "GetEditFieldContextParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GetEditFieldContextPayload>(
+                "GetEditFieldContextPayload",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static GetEditFieldContextParams {
-        static instance: ::protobuf::rt::LazyV2<GetEditFieldContextParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(GetEditFieldContextParams::new)
+    fn default_instance() -> &'static GetEditFieldContextPayload {
+        static instance: ::protobuf::rt::LazyV2<GetEditFieldContextPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(GetEditFieldContextPayload::new)
     }
 }
 
-impl ::protobuf::Clear for GetEditFieldContextParams {
+impl ::protobuf::Clear for GetEditFieldContextPayload {
     fn clear(&mut self) {
         self.grid_id.clear();
         self.one_of_field_id = ::std::option::Option::None;
@@ -1480,13 +1480,245 @@ impl ::protobuf::Clear for GetEditFieldContextParams {
     }
 }
 
-impl ::std::fmt::Debug for GetEditFieldContextParams {
+impl ::std::fmt::Debug for GetEditFieldContextPayload {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for GetEditFieldContextParams {
+impl ::protobuf::reflect::ProtobufValue for GetEditFieldContextPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct EditFieldPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub field_id: ::std::string::String,
+    pub field_type: super::meta::FieldType,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a EditFieldPayload {
+    fn default() -> &'a EditFieldPayload {
+        <EditFieldPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl EditFieldPayload {
+    pub fn new() -> EditFieldPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string field_id = 2;
+
+
+    pub fn get_field_id(&self) -> &str {
+        &self.field_id
+    }
+    pub fn clear_field_id(&mut self) {
+        self.field_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_id(&mut self, v: ::std::string::String) {
+        self.field_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
+        &mut self.field_id
+    }
+
+    // Take field
+    pub fn take_field_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+    }
+
+    // .FieldType field_type = 3;
+
+
+    pub fn get_field_type(&self) -> super::meta::FieldType {
+        self.field_type
+    }
+    pub fn clear_field_type(&mut self) {
+        self.field_type = super::meta::FieldType::RichText;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_type(&mut self, v: super::meta::FieldType) {
+        self.field_type = v;
+    }
+}
+
+impl ::protobuf::Message for EditFieldPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 3, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.field_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.field_id);
+        }
+        if self.field_type != super::meta::FieldType::RichText {
+            my_size += ::protobuf::rt::enum_size(3, self.field_type);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.field_id.is_empty() {
+            os.write_string(2, &self.field_id)?;
+        }
+        if self.field_type != super::meta::FieldType::RichText {
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.field_type))?;
+        }
+        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() -> EditFieldPayload {
+        EditFieldPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &EditFieldPayload| { &m.grid_id },
+                |m: &mut EditFieldPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "field_id",
+                |m: &EditFieldPayload| { &m.field_id },
+                |m: &mut EditFieldPayload| { &mut m.field_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::meta::FieldType>>(
+                "field_type",
+                |m: &EditFieldPayload| { &m.field_type },
+                |m: &mut EditFieldPayload| { &mut m.field_type },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<EditFieldPayload>(
+                "EditFieldPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static EditFieldPayload {
+        static instance: ::protobuf::rt::LazyV2<EditFieldPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(EditFieldPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for EditFieldPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.field_id.clear();
+        self.field_type = super::meta::FieldType::RichText;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for EditFieldPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for EditFieldPayload {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -5513,49 +5745,52 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x07grid_id\x18\x02\x20\x01(\tR\x06gridId\"K\n\x15FieldIdentifierParams\
     \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\
     id\x18\x02\x20\x01(\tR\x06gridId\"'\n\nFieldOrder\x12\x19\n\x08field_id\
-    \x18\x01\x20\x01(\tR\x07fieldId\"\x8f\x01\n\x19GetEditFieldContextParams\
-    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1b\n\x08field_i\
-    d\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\n\nfield_type\x18\x03\x20\x01(\
-    \x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fone_of_field_id\"|\n\x10EditFiel\
-    dContext\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid\
-    _field\x18\x02\x20\x01(\x0b2\x06.FieldR\tgridField\x12(\n\x10type_option\
-    _data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\x12\
-    \x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12Repeat\
-    edFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05it\
-    ems\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\
-    \x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\x12\x16\n\x06heigh\
-    t\x18\x03\x20\x01(\x05R\x06height\"\xb8\x01\n\x03Row\x12\x0e\n\x02id\x18\
-    \x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_field_id\x18\x02\x20\x03(\x0b2\
-    \x17.Row.CellByFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x03\
-    \x20\x01(\x05R\x06height\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\x03key\
-    \x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\x05\
-    .CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\x01\
-    \x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\x05\
-    items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"+\n\x0eGridBlockOrder\
-    \x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\"E\n\tGridBlock\
-    \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\
-    \x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\
-    \x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\
-    \x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b\
-    2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\
-    \x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\
-    \x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01\
-    (\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\
-    \x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstart\
-    RowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12CreateFieldPayload\
-    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\
-    \x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\
-    \x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\
-    \x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\
-    \x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\
-    \x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\
-    \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\
-    \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\
-    \x0f.GridBlockOrderR\x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\
-    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\
-    \x20\x01(\tR\x07blockId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\
-    \"<\n\x19CreateSelectOptionPayload\x12\x1f\n\x0boption_name\x18\x01\x20\
-    \x01(\tR\noptionNameb\x06proto3\
+    \x18\x01\x20\x01(\tR\x07fieldId\"\x90\x01\n\x1aGetEditFieldContextPayloa\
+    d\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1b\n\x08field_\
+    id\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\n\nfield_type\x18\x03\x20\x01(\
+    \x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fone_of_field_id\"q\n\x10EditFiel\
+    dPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\
+    \x08field_id\x18\x02\x20\x01(\tR\x07fieldId\x12)\n\nfield_type\x18\x03\
+    \x20\x01(\x0e2\n.FieldTypeR\tfieldType\"|\n\x10EditFieldContext\x12\x17\
+    \n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\
+    \x20\x01(\x0b2\x06.FieldR\tgridField\x12(\n\x10type_option_data\x18\x03\
+    \x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\x12\x1c\n\x05items\
+    \x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\
+    \x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08\
+    RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08b\
+    lock_id\x18\x02\x20\x01(\tR\x07blockId\x12\x16\n\x06height\x18\x03\x20\
+    \x01(\x05R\x06height\"\xb8\x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\
+    \tR\x02id\x12@\n\x10cell_by_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellB\
+    yFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\
+    \x06height\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01\
+    (\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\
+    \x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\
+    \x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\x05items\x18\x01\
+    \x20\x03(\x0b2\n.GridBlockR\x05items\"+\n\x0eGridBlockOrder\x12\x19\n\
+    \x08block_id\x18\x01\x20\x01(\tR\x07blockId\"E\n\tGridBlock\x12\x0e\n\
+    \x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b\
+    2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\
+    \x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\
+    \"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\
+    \x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\t\
+    R\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05va\
+    lue\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\
+    \"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\
+    idId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\
+    \x13one_of_start_row_id\"\xb6\x01\n\x12CreateFieldPayload\x12\x17\n\x07g\
+    rid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\
+    \x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\
+    \x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\
+    \x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\x11QueryFieldPaylo\
+    ad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_or\
+    ders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\
+    \x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06g\
+    ridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\
+    \x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\
+    \x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07bloc\
+    kId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\"<\n\x19CreateSelec\
+    tOptionPayload\x12\x1f\n\x0boption_name\x18\x01\x20\x01(\tR\noptionNameb\
+    \x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 6 - 1
shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

@@ -26,11 +26,16 @@ message FieldIdentifierParams {
 message FieldOrder {
     string field_id = 1;
 }
-message GetEditFieldContextParams {
+message GetEditFieldContextPayload {
     string grid_id = 1;
     oneof one_of_field_id { string field_id = 2; };
     FieldType field_type = 3;
 }
+message EditFieldPayload {
+    string grid_id = 1;
+    string field_id = 2;
+    FieldType field_type = 3;
+}
 message EditFieldContext {
     string grid_id = 1;
     Field grid_field = 2;

+ 10 - 2
shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

@@ -20,6 +20,10 @@ pub struct GridMetaPad {
     pub(crate) delta: GridMetaDelta,
 }
 
+pub trait TypeOptionDataDeserializer {
+    fn deserialize(&self, type_option_data: Vec<u8>) -> CollaborateResult<String>;
+}
+
 impl GridMetaPad {
     pub fn from_delta(delta: GridMetaDelta) -> CollaborateResult<Self> {
         let s = delta.to_str()?;
@@ -85,7 +89,11 @@ impl GridMetaPad {
         })
     }
 
-    pub fn update_field(&mut self, changeset: FieldChangesetParams) -> CollaborateResult<Option<GridChangeset>> {
+    pub fn update_field<T: TypeOptionDataDeserializer>(
+        &mut self,
+        changeset: FieldChangesetParams,
+        deserializer: T,
+    ) -> CollaborateResult<Option<GridChangeset>> {
         let field_id = changeset.field_id.clone();
         self.modify_field(&field_id, |field| {
             let mut is_changed = None;
@@ -120,7 +128,7 @@ impl GridMetaPad {
             }
 
             if let Some(type_option_data) = changeset.type_option_data {
-                match String::from_utf8(type_option_data) {
+                match deserializer.deserialize(type_option_data) {
                     Ok(type_option_json) => {
                         field.type_option_json = type_option_json;
                         is_changed = Some(())