Browse Source

refactor: CellCalendar with bloc

appflowy 3 years ago
parent
commit
f21ca6c52a
23 changed files with 575 additions and 402 deletions
  1. 2 2
      frontend/app_flowy/lib/startup/deps_resolver.dart
  2. 21 17
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart
  3. 95 0
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart
  4. 0 2
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart
  5. 6 6
      frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart
  6. 2 2
      frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart
  7. 8 5
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart
  8. 29 18
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  9. 5 6
      frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart
  10. 3 4
      frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart
  11. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart
  12. 0 217
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart
  13. 139 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart
  14. 90 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart
  15. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart
  16. 2 2
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  17. 13 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  18. 5 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  19. 14 19
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  20. 2 2
      frontend/rust-lib/flowy-grid/src/event_map.rs
  21. 5 5
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  22. 131 90
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  23. 1 1
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

+ 2 - 2
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -153,13 +153,13 @@ void _resolveGridDeps(GetIt getIt) {
   getIt.registerFactoryParam<FieldActionSheetBloc, GridFieldCellContext, void>(
     (data, _) => FieldActionSheetBloc(
       field: data.field,
-      service: FieldService(gridId: data.gridId),
+      fieldService: FieldService(gridId: data.gridId, fieldId: data.field.id),
     ),
   );
 
   getIt.registerFactoryParam<FieldEditorBloc, String, EditFieldContextLoader>(
     (gridId, fieldLoader) => FieldEditorBloc(
-      service: FieldService(gridId: gridId),
+      gridId: gridId,
       fieldLoader: fieldLoader,
     ),
   );

+ 21 - 17
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:collection';
 
 import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
+import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:equatable/equatable.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -12,7 +13,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
 
 part 'cell_service.freezed.dart';
@@ -59,15 +59,16 @@ class GridCellContextBuilder {
 }
 
 // ignore: must_be_immutable
-class GridCellContext<C> extends Equatable {
+class GridCellContext<T> extends Equatable {
   final GridCell gridCell;
   final GridCellCache cellCache;
   final GridCellCacheKey _cacheKey;
-  final GridCellDataLoader<C> cellDataLoader;
+  final GridCellDataLoader<T> cellDataLoader;
   final CellService _cellService = CellService();
+  final FieldService _fieldService;
 
   late final CellListener _cellListener;
-  late final ValueNotifier<C?> _cellDataNotifier;
+  late final ValueNotifier<T?> _cellDataNotifier;
   bool isListening = false;
   VoidCallback? _onFieldChangedFn;
   Timer? _delayOperation;
@@ -76,9 +77,10 @@ class GridCellContext<C> extends Equatable {
     required this.gridCell,
     required this.cellCache,
     required this.cellDataLoader,
-  }) : _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id);
+  })  : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
+        _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id);
 
-  GridCellContext<C> clone() {
+  GridCellContext<T> clone() {
     return GridCellContext(
       gridCell: gridCell,
       cellDataLoader: cellDataLoader,
@@ -98,16 +100,14 @@ class GridCellContext<C> extends Equatable {
 
   FieldType get fieldType => gridCell.field.fieldType;
 
-  GridCellCacheKey get cacheKey => _cacheKey;
-
-  VoidCallback? startListening({required void Function(C) onCellChanged}) {
+  VoidCallback? startListening({required void Function(T) onCellChanged}) {
     if (isListening) {
       Log.error("Already started. It seems like you should call clone first");
       return null;
     }
 
     isListening = true;
-    _cellDataNotifier = ValueNotifier(cellCache.get(cacheKey));
+    _cellDataNotifier = ValueNotifier(cellCache.get(_cacheKey));
     _cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
     _cellListener.start(onCellChanged: (result) {
       result.fold(
@@ -120,12 +120,12 @@ class GridCellContext<C> extends Equatable {
       _onFieldChangedFn = () {
         _loadData();
       };
-      cellCache.addListener(cacheKey, _onFieldChangedFn!);
+      cellCache.addListener(_cacheKey, _onFieldChangedFn!);
     }
 
     onCellChangedFn() {
       final value = _cellDataNotifier.value;
-      if (value is C) {
+      if (value is T) {
         onCellChanged(value);
       }
 
@@ -142,14 +142,18 @@ class GridCellContext<C> extends Equatable {
     _cellDataNotifier.removeListener(fn);
   }
 
-  C? getCellData() {
-    final data = cellCache.get(cacheKey);
+  T? getCellData() {
+    final data = cellCache.get(_cacheKey);
     if (data == null) {
       _loadData();
     }
     return data;
   }
 
+  Future<Either<List<int>, FlowyError>> getTypeOptionData() {
+    return _fieldService.getTypeOptionData(fieldType: fieldType);
+  }
+
   void saveCellData(String data) {
     _cellService.updateCell(gridId: gridId, fieldId: field.id, rowId: rowId, data: data).then((result) {
       result.fold((l) => null, (err) => Log.error(err));
@@ -161,7 +165,7 @@ class GridCellContext<C> extends Equatable {
     _delayOperation = Timer(const Duration(milliseconds: 10), () {
       cellDataLoader.loadData().then((data) {
         _cellDataNotifier.value = data;
-        cellCache.insert(GridCellCacheData(key: cacheKey, object: data));
+        cellCache.insert(GridCellCacheData(key: _cacheKey, object: data));
       });
     });
   }
@@ -170,13 +174,13 @@ class GridCellContext<C> extends Equatable {
     _delayOperation?.cancel();
 
     if (_onFieldChangedFn != null) {
-      cellCache.removeListener(cacheKey, _onFieldChangedFn!);
+      cellCache.removeListener(_cacheKey, _onFieldChangedFn!);
       _onFieldChangedFn = null;
     }
   }
 
   @override
-  List<Object> get props => [cellCache.get(cacheKey) ?? "", cellId];
+  List<Object> get props => [cellCache.get(_cacheKey) ?? "", cellId];
 }
 
 abstract class GridCellDataLoader<T> {

+ 95 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart

@@ -0,0 +1,95 @@
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
+import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:table_calendar/table_calendar.dart';
+import 'dart:async';
+import 'cell_service.dart';
+import 'package:dartz/dartz.dart';
+part 'date_cal_bloc.freezed.dart';
+
+class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
+  final GridDefaultCellContext cellContext;
+  void Function()? _onCellChangedFn;
+
+  DateCalBloc({required this.cellContext}) : super(DateCalState.initial(cellContext)) {
+    on<DateCalEvent>(
+      (event, emit) async {
+        event.map(
+          initial: (_Initial value) {
+            _startListening();
+          },
+          selectDay: (_SelectDay value) {
+            if (!isSameDay(state.selectedDay, value.day)) {
+              _updateCellData(value.day);
+              emit(state.copyWith(selectedDay: value.day));
+            }
+          },
+          setFormat: (_CalendarFormat value) {
+            emit(state.copyWith(format: value.format));
+          },
+          setFocusedDay: (_FocusedDay value) {
+            emit(state.copyWith(focusedDay: value.day));
+          },
+          didReceiveCellUpdate: (_DidReceiveCellUpdate value) {},
+          didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
+            emit(state.copyWith(field: value.field));
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    if (_onCellChangedFn != null) {
+      cellContext.removeListener(_onCellChangedFn!);
+      _onCellChangedFn = null;
+    }
+    cellContext.dispose();
+    return super.close();
+  }
+
+  void _startListening() {
+    _onCellChangedFn = cellContext.startListening(
+      onCellChanged: ((cell) {
+        if (!isClosed) {
+          add(DateCalEvent.didReceiveCellUpdate(cell));
+        }
+      }),
+    );
+  }
+
+  void _updateCellData(DateTime day) {
+    final data = day.millisecondsSinceEpoch ~/ 1000;
+    cellContext.saveCellData(data.toString());
+  }
+}
+
+@freezed
+class DateCalEvent with _$DateCalEvent {
+  const factory DateCalEvent.initial() = _Initial;
+  const factory DateCalEvent.selectDay(DateTime day) = _SelectDay;
+  const factory DateCalEvent.setFormat(CalendarFormat format) = _CalendarFormat;
+  const factory DateCalEvent.setFocusedDay(DateTime day) = _FocusedDay;
+  const factory DateCalEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
+  const factory DateCalEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
+}
+
+@freezed
+class DateCalState with _$DateCalState {
+  const factory DateCalState({
+    required Field field,
+    required Option<DateTypeOption> typeOptinoData,
+    required CalendarFormat format,
+    required DateTime focusedDay,
+    DateTime? selectedDay,
+  }) = _DateCalState;
+
+  factory DateCalState.initial(GridCellContext context) => DateCalState(
+        field: context.field,
+        typeOptinoData: none(),
+        format: CalendarFormat.month,
+        focusedDay: DateTime.now(),
+      );
+}

+ 0 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart

@@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'cell_service.dart';
-
 part 'date_cell_bloc.freezed.dart';
 
 class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@@ -72,7 +71,6 @@ class DateCellState with _$DateCellState {
   const factory DateCellState({
     required String content,
     required Field field,
-    DateTime? selectedDay,
   }) = _DateCellState;
 
   factory DateCellState.initial(GridCellContext context) => DateCellState(

+ 6 - 6
frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart

@@ -8,36 +8,36 @@ import 'field_service.dart';
 part 'field_action_sheet_bloc.freezed.dart';
 
 class FieldActionSheetBloc extends Bloc<FieldActionSheetEvent, FieldActionSheetState> {
-  final FieldService service;
+  final FieldService fieldService;
 
-  FieldActionSheetBloc({required Field field, required this.service})
+  FieldActionSheetBloc({required Field field, required this.fieldService})
       : super(FieldActionSheetState.initial(EditFieldContext.create()..gridField = field)) {
     on<FieldActionSheetEvent>(
       (event, emit) async {
         await event.map(
           updateFieldName: (_UpdateFieldName value) async {
-            final result = await service.updateField(fieldId: field.id, name: value.name);
+            final result = await fieldService.updateField(name: value.name);
             result.fold(
               (l) => null,
               (err) => Log.error(err),
             );
           },
           hideField: (_HideField value) async {
-            final result = await service.updateField(fieldId: field.id, visibility: false);
+            final result = await fieldService.updateField(visibility: false);
             result.fold(
               (l) => null,
               (err) => Log.error(err),
             );
           },
           deleteField: (_DeleteField value) async {
-            final result = await service.deleteField(fieldId: field.id);
+            final result = await fieldService.deleteField();
             result.fold(
               (l) => null,
               (err) => Log.error(err),
             );
           },
           duplicateField: (_DuplicateField value) async {
-            final result = await service.duplicateField(fieldId: field.id);
+            final result = await fieldService.duplicateField();
             result.fold(
               (l) => null,
               (err) => Log.error(err),

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart

@@ -15,7 +15,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
   FieldCellBloc({
     required GridFieldCellContext cellContext,
   })  : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
-        _fieldService = FieldService(gridId: cellContext.gridId),
+        _fieldService = FieldService(gridId: cellContext.gridId, fieldId: cellContext.field.id),
         super(FieldCellState.initial(cellContext)) {
     on<FieldCellEvent>(
       (event, emit) async {
@@ -30,7 +30,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
             final defaultWidth = state.field.width.toDouble();
             final width = defaultWidth + value.offset;
             if (width > defaultWidth && width < 300) {
-              _fieldService.updateField(fieldId: state.field.id, width: width);
+              _fieldService.updateField(width: width);
             }
           },
         );

+ 8 - 5
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart

@@ -11,14 +11,14 @@ import 'package:protobuf/protobuf.dart';
 part 'field_editor_bloc.freezed.dart';
 
 class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
-  final FieldService service;
+  final String gridId;
   final EditFieldContextLoader _loader;
 
   FieldEditorBloc({
-    required this.service,
+    required this.gridId,
     required EditFieldContextLoader fieldLoader,
   })  : _loader = fieldLoader,
-        super(FieldEditorState.initial(service.gridId)) {
+        super(FieldEditorState.initial(gridId)) {
     on<FieldEditorEvent>(
       (event, emit) async {
         await event.map(
@@ -73,7 +73,9 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
             newContext.typeOptionData = typeOptionData;
           }
         });
-        service.insertField(
+
+        FieldService.insertField(
+          gridId: gridId,
           field: newContext.gridField,
           typeOptionData: newContext.typeOptionData,
         );
@@ -87,7 +89,8 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
     await state.editFieldContext.fold(
       () async => null,
       (context) async {
-        final result = await service.insertField(
+        final result = await FieldService.insertField(
+          gridId: gridId,
           field: context.gridField,
           typeOptionData: context.typeOptionData,
         );

+ 29 - 18
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -8,10 +8,11 @@ part 'field_service.freezed.dart';
 
 class FieldService {
   final String gridId;
+  final String fieldId;
 
-  FieldService({required this.gridId});
+  FieldService({required this.gridId, required this.fieldId});
 
-  Future<Either<EditFieldContext, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
+  Future<Either<EditFieldContext, FlowyError>> switchToField(FieldType fieldType) {
     final payload = EditFieldPayload.create()
       ..gridId = gridId
       ..fieldId = fieldId
@@ -20,8 +21,8 @@ class FieldService {
     return GridEventSwitchToField(payload).send();
   }
 
-  Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(String fieldId, FieldType fieldType) {
-    final payload = GetEditFieldContextPayload.create()
+  Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(FieldType fieldType) {
+    final payload = EditFieldPayload.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..fieldType = fieldType;
@@ -29,7 +30,7 @@ class FieldService {
     return GridEventGetEditFieldContext(payload).send();
   }
 
-  Future<Either<Unit, FlowyError>> moveField(String fieldId, int fromIndex, int toIndex) {
+  Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) {
     final payload = MoveItemPayload.create()
       ..gridId = gridId
       ..itemId = fieldId
@@ -41,7 +42,6 @@ class FieldService {
   }
 
   Future<Either<Unit, FlowyError>> updateField({
-    required String fieldId,
     String? name,
     FieldType? fieldType,
     bool? frozen,
@@ -81,7 +81,8 @@ class FieldService {
   }
 
   // Create the field if it does not exist. Otherwise, update the field.
-  Future<Either<Unit, FlowyError>> insertField({
+  static Future<Either<Unit, FlowyError>> insertField({
+    required String gridId,
     required Field field,
     List<int>? typeOptionData,
     String? startFieldId,
@@ -98,9 +99,7 @@ class FieldService {
     return GridEventInsertField(payload).send();
   }
 
-  Future<Either<Unit, FlowyError>> deleteField({
-    required String fieldId,
-  }) {
+  Future<Either<Unit, FlowyError>> deleteField() {
     final payload = FieldIdentifierPayload.create()
       ..gridId = gridId
       ..fieldId = fieldId;
@@ -108,15 +107,27 @@ class FieldService {
     return GridEventDeleteField(payload).send();
   }
 
-  Future<Either<Unit, FlowyError>> duplicateField({
-    required String fieldId,
-  }) {
+  Future<Either<Unit, FlowyError>> duplicateField() {
     final payload = FieldIdentifierPayload.create()
       ..gridId = gridId
       ..fieldId = fieldId;
 
     return GridEventDuplicateField(payload).send();
   }
+
+  Future<Either<List<int>, FlowyError>> getTypeOptionData({
+    required FieldType fieldType,
+  }) {
+    final payload = EditFieldPayload.create()
+      ..fieldId = fieldId
+      ..fieldType = fieldType;
+    return GridEventGetFieldTypeOption(payload).send().then((result) {
+      return result.fold(
+        (data) => left(data.typeOptionData),
+        (err) => right(err),
+      );
+    });
+  }
 }
 
 @freezed
@@ -141,7 +152,7 @@ class NewFieldContextLoader extends EditFieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> load() {
-    final payload = GetEditFieldContextPayload.create()
+    final payload = EditFieldPayload.create()
       ..gridId = gridId
       ..fieldType = FieldType.RichText;
 
@@ -150,7 +161,7 @@ class NewFieldContextLoader extends EditFieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
-    final payload = GetEditFieldContextPayload.create()
+    final payload = EditFieldPayload.create()
       ..gridId = gridId
       ..fieldType = fieldType;
 
@@ -169,7 +180,7 @@ class FieldContextLoaderAdaptor extends EditFieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> load() {
-    final payload = GetEditFieldContextPayload.create()
+    final payload = EditFieldPayload.create()
       ..gridId = gridId
       ..fieldId = field.id
       ..fieldType = field.fieldType;
@@ -179,7 +190,7 @@ class FieldContextLoaderAdaptor extends EditFieldContextLoader {
 
   @override
   Future<Either<EditFieldContext, FlowyError>> switchToField(String fieldId, FieldType fieldType) async {
-    final fieldService = FieldService(gridId: gridId);
-    return fieldService.switchToField(fieldId, fieldType);
+    final fieldService = FieldService(gridId: gridId, fieldId: fieldId);
+    return fieldService.switchToField(fieldType);
   }
 }

+ 5 - 6
frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart

@@ -9,14 +9,13 @@ import 'grid_service.dart';
 part 'grid_header_bloc.freezed.dart';
 
 class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
-  final FieldService _fieldService;
   final GridFieldCache fieldCache;
+  final String gridId;
 
   GridHeaderBloc({
-    required String gridId,
+    required this.gridId,
     required this.fieldCache,
-  })  : _fieldService = FieldService(gridId: gridId),
-        super(GridHeaderState.initial(fieldCache.clonedFields)) {
+  }) : super(GridHeaderState.initial(fieldCache.clonedFields)) {
     on<GridHeaderEvent>(
       (event, emit) async {
         await event.map(
@@ -39,8 +38,8 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
     fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
     emit(state.copyWith(fields: fields));
 
-    final result = await _fieldService.moveField(
-      value.field.id,
+    final fieldService = FieldService(gridId: gridId, fieldId: value.field.id);
+    final result = await fieldService.moveField(
       value.fromIndex,
       value.toIndex,
     );

+ 3 - 4
frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart

@@ -9,13 +9,11 @@ import 'dart:async';
 part 'property_bloc.freezed.dart';
 
 class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
-  final FieldService _service;
   final GridFieldCache _fieldCache;
   Function()? _listenFieldCallback;
 
   GridPropertyBloc({required String gridId, required GridFieldCache fieldCache})
-      : _service = FieldService(gridId: gridId),
-        _fieldCache = fieldCache,
+      : _fieldCache = fieldCache,
         super(GridPropertyState.initial(gridId, fieldCache.clonedFields)) {
     on<GridPropertyEvent>(
       (event, emit) async {
@@ -24,7 +22,8 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
             _startListening();
           },
           setFieldVisibility: (_SetFieldVisibility value) async {
-            final result = await _service.updateField(fieldId: value.fieldId, visibility: value.visibility);
+            final fieldService = FieldService(gridId: gridId, fieldId: value.fieldId);
+            final result = await fieldService.updateField(visibility: value.visibility);
             result.fold(
               (l) => null,
               (err) => Log.error(err),

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

@@ -9,7 +9,7 @@ import 'package:provider/provider.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'checkbox_cell.dart';
-import 'date_cell.dart';
+import 'date_cell/date_cell.dart';
 import 'number_cell.dart';
 import 'selection_cell/selection_cell.dart';
 import 'text_cell.dart';

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

@@ -1,217 +0,0 @@
-import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/prelude.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:table_calendar/table_calendar.dart';
-import 'cell_builder.dart';
-
-class DateCellStyle extends GridCellStyle {
-  Alignment alignment;
-
-  DateCellStyle({this.alignment = Alignment.center});
-}
-
-abstract class GridCellDelegate {
-  void onFocus(bool isFocus);
-  GridCellDelegate get delegate;
-}
-
-class DateCell extends GridCellWidget {
-  final GridCellContextBuilder cellContextBuilder;
-  late final DateCellStyle? cellStyle;
-
-  DateCell({
-    GridCellStyle? style,
-    required this.cellContextBuilder,
-    Key? key,
-  }) : super(key: key) {
-    if (style != null) {
-      cellStyle = (style as DateCellStyle);
-    } else {
-      cellStyle = null;
-    }
-  }
-
-  @override
-  State<DateCell> createState() => _DateCellState();
-}
-
-class _DateCellState extends State<DateCell> {
-  late DateCellBloc _cellBloc;
-
-  @override
-  void initState() {
-    final cellContext = widget.cellContextBuilder.build();
-    _cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
-    super.initState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final alignment = widget.cellStyle != null ? widget.cellStyle!.alignment : Alignment.center;
-    return BlocProvider.value(
-      value: _cellBloc,
-      child: BlocBuilder<DateCellBloc, DateCellState>(
-        builder: (context, state) {
-          return SizedBox.expand(
-            child: GestureDetector(
-              behavior: HitTestBehavior.opaque,
-              onTap: () {
-                widget.onFocus.value = true;
-                _CellCalendar.show(
-                  context,
-                  onSelected: (day) {
-                    context.read<DateCellBloc>().add(DateCellEvent.selectDay(day));
-                  },
-                  onDismissed: () => widget.onFocus.value = false,
-                );
-              },
-              child: MouseRegion(
-                opaque: false,
-                cursor: SystemMouseCursors.click,
-                child: Align(alignment: alignment, child: FlowyText.medium(state.content, fontSize: 12)),
-              ),
-            ),
-          );
-        },
-      ),
-    );
-  }
-
-  @override
-  Future<void> dispose() async {
-    _cellBloc.close();
-    super.dispose();
-  }
-}
-
-final kToday = DateTime.now();
-final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
-final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
-
-class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
-  final void Function(DateTime) onSelected;
-  final VoidCallback onDismissed;
-  final bool includeTime;
-  const _CellCalendar({
-    required this.onSelected,
-    required this.onDismissed,
-    required this.includeTime,
-    Key? key,
-  }) : super(key: key);
-
-  @override
-  State<_CellCalendar> createState() => _CellCalendarState();
-
-  static Future<void> show(
-    BuildContext context, {
-    required void Function(DateTime) onSelected,
-    required VoidCallback onDismissed,
-  }) async {
-    _CellCalendar.remove(context);
-
-    final calendar = _CellCalendar(
-      onSelected: onSelected,
-      onDismissed: onDismissed,
-      includeTime: false,
-    );
-    // const size = Size(460, 400);
-    // final window = await getWindowInfo();
-    // FlowyOverlay.of(context).insertWithRect(
-    //   widget: OverlayContainer(
-    //     child: calendar,
-    //     constraints: BoxConstraints.loose(const Size(460, 400)),
-    //   ),
-    //   identifier: _CellCalendar.identifier(),
-    //   anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
-    //   anchorSize: window.frame.size,
-    //   anchorDirection: AnchorDirection.center,
-    //   style: FlowyOverlayStyle(blur: false),
-    //   delegate: calendar,
-    // );
-
-    FlowyOverlay.of(context).insertWithAnchor(
-      widget: OverlayContainer(
-        child: calendar,
-        constraints: BoxConstraints.tight(const Size(320, 320)),
-      ),
-      identifier: _CellCalendar.identifier(),
-      anchorContext: context,
-      anchorDirection: AnchorDirection.leftWithCenterAligned,
-      style: FlowyOverlayStyle(blur: false),
-      delegate: calendar,
-    );
-  }
-
-  static void remove(BuildContext context) {
-    FlowyOverlay.of(context).remove(identifier());
-  }
-
-  static String identifier() {
-    return (_CellCalendar).toString();
-  }
-
-  @override
-  void didRemove() => onDismissed();
-}
-
-class _CellCalendarState extends State<_CellCalendar> {
-  CalendarFormat _calendarFormat = CalendarFormat.month;
-  DateTime _focusedDay = DateTime.now();
-  DateTime? _selectedDay;
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-    return TableCalendar(
-      firstDay: kFirstDay,
-      lastDay: kLastDay,
-      focusedDay: _focusedDay,
-      rowHeight: 40,
-      calendarFormat: _calendarFormat,
-      headerStyle: const HeaderStyle(formatButtonVisible: false),
-      calendarStyle: CalendarStyle(
-        selectedDecoration: BoxDecoration(
-          color: theme.main1,
-          shape: BoxShape.circle,
-        ),
-        todayDecoration: BoxDecoration(
-          color: theme.shader4,
-          shape: BoxShape.circle,
-        ),
-        selectedTextStyle: TextStyle(
-          color: theme.surface,
-          fontSize: 14.0,
-        ),
-        todayTextStyle: TextStyle(
-          color: theme.surface,
-          fontSize: 14.0,
-        ),
-      ),
-      selectedDayPredicate: (day) {
-        return isSameDay(_selectedDay, day);
-      },
-      onDaySelected: (selectedDay, focusedDay) {
-        if (!isSameDay(_selectedDay, selectedDay)) {
-          // Call `setState()` when updating the selected day
-          setState(() {
-            _selectedDay = selectedDay;
-            _focusedDay = focusedDay;
-            widget.onSelected(selectedDay);
-          });
-        }
-      },
-      onFormatChanged: (format) {
-        setState(() {
-          _calendarFormat = format;
-        });
-      },
-      onPageChanged: (focusedDay) {
-        _focusedDay = focusedDay;
-      },
-    );
-  }
-}

+ 139 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart

@@ -0,0 +1,139 @@
+import 'package:app_flowy/workspace/application/grid/cell/date_cal_bloc.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:table_calendar/table_calendar.dart';
+import 'package:app_flowy/workspace/application/grid/prelude.dart';
+
+final kToday = DateTime.now();
+final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
+final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
+
+class CellCalendar with FlowyOverlayDelegate {
+  final VoidCallback onDismissed;
+
+  const CellCalendar({
+    required this.onDismissed,
+  });
+
+  Future<void> show(
+    BuildContext context, {
+    required GridDefaultCellContext cellContext,
+    required void Function(DateTime) onSelected,
+  }) async {
+    CellCalendar.remove(context);
+
+    final calendar = _CellCalendarWidget(
+      onSelected: onSelected,
+      includeTime: false,
+      cellContext: cellContext,
+    );
+    // const size = Size(460, 400);
+    // final window = await getWindowInfo();
+    // FlowyOverlay.of(context).insertWithRect(
+    //   widget: OverlayContainer(
+    //     child: calendar,
+    //     constraints: BoxConstraints.loose(const Size(460, 400)),
+    //   ),
+    //   identifier: _CellCalendar.identifier(),
+    //   anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
+    //   anchorSize: window.frame.size,
+    //   anchorDirection: AnchorDirection.center,
+    //   style: FlowyOverlayStyle(blur: false),
+    //   delegate: calendar,
+    // );
+
+    FlowyOverlay.of(context).insertWithAnchor(
+      widget: OverlayContainer(
+        child: calendar,
+        constraints: BoxConstraints.tight(const Size(320, 320)),
+      ),
+      identifier: CellCalendar.identifier(),
+      anchorContext: context,
+      anchorDirection: AnchorDirection.leftWithCenterAligned,
+      style: FlowyOverlayStyle(blur: false),
+      delegate: this,
+    );
+  }
+
+  static void remove(BuildContext context) {
+    FlowyOverlay.of(context).remove(identifier());
+  }
+
+  static String identifier() {
+    return (CellCalendar).toString();
+  }
+
+  @override
+  void didRemove() => onDismissed();
+}
+
+class _CellCalendarWidget extends StatelessWidget {
+  final bool includeTime;
+  final GridDefaultCellContext cellContext;
+  final void Function(DateTime) onSelected;
+
+  const _CellCalendarWidget({
+    required this.onSelected,
+    required this.includeTime,
+    required this.cellContext,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+    return BlocProvider(
+      create: (context) => DateCalBloc(cellContext: cellContext)..add(const DateCalEvent.initial()),
+      child: BlocConsumer<DateCalBloc, DateCalState>(
+        listener: (context, state) {
+          if (state.selectedDay != null) {
+            onSelected(state.selectedDay!);
+          }
+        },
+        listenWhen: (p, c) => p.selectedDay != c.selectedDay,
+        builder: (context, state) {
+          return TableCalendar(
+            firstDay: kFirstDay,
+            lastDay: kLastDay,
+            focusedDay: state.focusedDay,
+            rowHeight: 40,
+            calendarFormat: state.format,
+            headerStyle: const HeaderStyle(formatButtonVisible: false),
+            calendarStyle: CalendarStyle(
+              selectedDecoration: BoxDecoration(
+                color: theme.main1,
+                shape: BoxShape.circle,
+              ),
+              todayDecoration: BoxDecoration(
+                color: theme.shader4,
+                shape: BoxShape.circle,
+              ),
+              selectedTextStyle: TextStyle(
+                color: theme.surface,
+                fontSize: 14.0,
+              ),
+              todayTextStyle: TextStyle(
+                color: theme.surface,
+                fontSize: 14.0,
+              ),
+            ),
+            selectedDayPredicate: (day) {
+              return isSameDay(state.selectedDay, day);
+            },
+            onDaySelected: (selectedDay, focusedDay) {
+              context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
+            },
+            onFormatChanged: (format) {
+              context.read<DateCalBloc>().add(DateCalEvent.setFormat(format));
+            },
+            onPageChanged: (focusedDay) {
+              context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay));
+            },
+          );
+        },
+      ),
+    );
+  }
+}

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

@@ -0,0 +1,90 @@
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/application/grid/prelude.dart';
+
+import '../cell_builder.dart';
+import 'calendar.dart';
+
+class DateCellStyle extends GridCellStyle {
+  Alignment alignment;
+
+  DateCellStyle({this.alignment = Alignment.center});
+}
+
+abstract class GridCellDelegate {
+  void onFocus(bool isFocus);
+  GridCellDelegate get delegate;
+}
+
+class DateCell extends GridCellWidget {
+  final GridCellContextBuilder cellContextBuilder;
+  late final DateCellStyle? cellStyle;
+
+  DateCell({
+    GridCellStyle? style,
+    required this.cellContextBuilder,
+    Key? key,
+  }) : super(key: key) {
+    if (style != null) {
+      cellStyle = (style as DateCellStyle);
+    } else {
+      cellStyle = null;
+    }
+  }
+
+  @override
+  State<DateCell> createState() => _DateCellState();
+}
+
+class _DateCellState extends State<DateCell> {
+  late DateCellBloc _cellBloc;
+
+  @override
+  void initState() {
+    final cellContext = widget.cellContextBuilder.build();
+    _cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final alignment = widget.cellStyle != null ? widget.cellStyle!.alignment : Alignment.center;
+    return BlocProvider.value(
+      value: _cellBloc,
+      child: BlocBuilder<DateCellBloc, DateCellState>(
+        builder: (context, state) {
+          return SizedBox.expand(
+            child: GestureDetector(
+              behavior: HitTestBehavior.opaque,
+              onTap: () => _showCalendar(context),
+              child: MouseRegion(
+                opaque: false,
+                cursor: SystemMouseCursors.click,
+                child: Align(alignment: alignment, child: FlowyText.medium(state.content, fontSize: 12)),
+              ),
+            ),
+          );
+        },
+      ),
+    );
+  }
+
+  void _showCalendar(BuildContext context) {
+    final bloc = context.read<DateCellBloc>();
+    widget.onFocus.value = true;
+    final calendar = CellCalendar(onDismissed: () => widget.onFocus.value = false);
+    calendar.show(
+      context,
+      cellContext: bloc.cellContext.clone(),
+      onSelected: (day) => bloc.add(DateCellEvent.selectDay(day)),
+    );
+  }
+
+  @override
+  Future<void> dispose() async {
+    _cellBloc.close();
+    super.dispose();
+  }
+}

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

@@ -1,6 +1,6 @@
 export 'cell_builder.dart';
 export 'text_cell.dart';
 export 'number_cell.dart';
-export 'date_cell.dart';
+export 'date_cell/date_cell.dart';
 export 'checkbox_cell.dart';
 export 'selection_cell/selection_cell.dart';

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

@@ -138,7 +138,7 @@ class GridEventDuplicateField {
 }
 
 class GridEventGetEditFieldContext {
-     GetEditFieldContextPayload request;
+     EditFieldPayload request;
      GridEventGetEditFieldContext(this.request);
 
     Future<Either<EditFieldContext, FlowyError>> send() {
@@ -172,7 +172,7 @@ class GridEventMoveItem {
 }
 
 class GridEventGetFieldTypeOption {
-     GetEditFieldContextPayload request;
+     EditFieldPayload request;
      GridEventGetFieldTypeOption(this.request);
 
     Future<Either<FieldTypeOptionData, FlowyError>> send() {

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

@@ -490,8 +490,18 @@ class GetEditFieldContextPayload extends $pb.GeneratedMessage {
   void clearFieldType() => clearField(3);
 }
 
+enum EditFieldPayload_OneOfFieldId {
+  fieldId, 
+  notSet
+}
+
 class EditFieldPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, EditFieldPayload_OneOfFieldId> _EditFieldPayload_OneOfFieldIdByTag = {
+    2 : EditFieldPayload_OneOfFieldId.fieldId,
+    0 : EditFieldPayload_OneOfFieldId.notSet
+  };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldPayload', 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')
     ..e<FieldType>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values)
@@ -537,6 +547,9 @@ class EditFieldPayload extends $pb.GeneratedMessage {
   static EditFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EditFieldPayload>(create);
   static EditFieldPayload? _defaultInstance;
 
+  EditFieldPayload_OneOfFieldId whichOneOfFieldId() => _EditFieldPayload_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfFieldId() => clearField($_whichOneof(0));
+
   @$pb.TagNumber(1)
   $core.String get gridId => $_getSZ(0);
   @$pb.TagNumber(1)

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

@@ -117,13 +117,16 @@ 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_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
     const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
   ],
+  '8': const [
+    const {'1': 'one_of_field_id'},
+  ],
 };
 
 /// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGU=');
+final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIbCghmaWVsZF9pZBgCIAEoCUgAUgdmaWVsZElkEikKCmZpZWxkX3R5cGUYAyABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZUIRCg9vbmVfb2ZfZmllbGRfaWQ=');
 @$core.Deprecated('Use editFieldContextDescriptor instead')
 const EditFieldContext$json = const {
   '1': 'EditFieldContext',

+ 14 - 19
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -87,20 +87,15 @@ pub(crate) async fn switch_to_field_handler(
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<EditFieldContext, FlowyError> {
     let params: EditFieldParams = data.into_inner().try_into()?;
+    if params.field_id.is_none() {
+        return Err(ErrorCode::FieldIdIsEmpty.into());
+    }
+    let field_id = params.field_id.unwrap();
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    editor
-        .switch_to_field_type(&params.field_id, &params.field_type)
-        .await?;
-
-    let field_meta = editor.get_field_meta(&params.field_id).await;
-    let edit_context = make_field_edit_context(
-        &params.grid_id,
-        Some(params.field_id),
-        params.field_type,
-        editor,
-        field_meta,
-    )
-    .await?;
+    editor.switch_to_field_type(&field_id, &params.field_type).await?;
+    let field_meta = editor.get_field_meta(&field_id).await;
+    let edit_context =
+        make_edit_field_context(&params.grid_id, Some(field_id), params.field_type, editor, field_meta).await?;
     data_result(edit_context)
 }
 
@@ -117,23 +112,23 @@ pub(crate) async fn duplicate_field_handler(
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_field_context_handler(
-    data: Data<GetEditFieldContextPayload>,
+    data: Data<EditFieldPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<EditFieldContext, FlowyError> {
-    let params = data.into_inner();
+    let params: EditFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let edit_context =
-        make_field_edit_context(&params.grid_id, params.field_id, params.field_type, editor, None).await?;
+        make_edit_field_context(&params.grid_id, params.field_id, params.field_type, editor, None).await?;
 
     data_result(edit_context)
 }
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_field_type_option_data_handler(
-    data: Data<GetEditFieldContextPayload>,
+    data: Data<EditFieldPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<FieldTypeOptionData, FlowyError> {
-    let params = data.into_inner();
+    let params: EditFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let field_meta = get_or_create_field_meta(params.field_id, &params.field_type, editor).await?;
     let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?;
@@ -155,7 +150,7 @@ pub(crate) async fn move_item_handler(
     Ok(())
 }
 
-async fn make_field_edit_context(
+async fn make_edit_field_context(
     grid_id: &str,
     field_id: Option<String>,
     field_type: FieldType,

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

@@ -64,13 +64,13 @@ pub enum GridEvent {
     #[event(input = "FieldIdentifierPayload")]
     DuplicateField = 15,
 
-    #[event(input = "GetEditFieldContextPayload", output = "EditFieldContext")]
+    #[event(input = "EditFieldPayload", output = "EditFieldContext")]
     GetEditFieldContext = 16,
 
     #[event(input = "MoveItemPayload")]
     MoveItem = 17,
 
-    #[event(input = "GetEditFieldContextPayload", output = "FieldTypeOptionData")]
+    #[event(input = "EditFieldPayload", output = "FieldTypeOptionData")]
     GetFieldTypeOption = 18,
 
     #[event(input = "CreateSelectOptionPayload", output = "SelectOption")]

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

@@ -167,8 +167,8 @@ pub struct EditFieldPayload {
     #[pb(index = 1)]
     pub grid_id: String,
 
-    #[pb(index = 2)]
-    pub field_id: String,
+    #[pb(index = 2, one_of)]
+    pub field_id: Option<String>,
 
     #[pb(index = 3)]
     pub field_type: FieldType,
@@ -176,7 +176,7 @@ pub struct EditFieldPayload {
 
 pub struct EditFieldParams {
     pub grid_id: String,
-    pub field_id: String,
+    pub field_id: Option<String>,
     pub field_type: FieldType,
 }
 
@@ -185,10 +185,10 @@ impl TryInto<EditFieldParams> for EditFieldPayload {
 
     fn try_into(self) -> Result<EditFieldParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        // let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
         Ok(EditFieldParams {
             grid_id: grid_id.0,
-            field_id: field_id.0,
+            field_id: self.field_id,
             field_type: self.field_type,
         })
     }

+ 131 - 90
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -1644,8 +1644,9 @@ impl ::protobuf::reflect::ProtobufValue for GetEditFieldContextPayload {
 pub struct EditFieldPayload {
     // message fields
     pub grid_id: ::std::string::String,
-    pub field_id: ::std::string::String,
     pub field_type: FieldType,
+    // message oneof groups
+    pub one_of_field_id: ::std::option::Option<EditFieldPayload_oneof_one_of_field_id>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -1657,6 +1658,11 @@ impl<'a> ::std::default::Default for &'a EditFieldPayload {
     }
 }
 
+#[derive(Clone,PartialEq,Debug)]
+pub enum EditFieldPayload_oneof_one_of_field_id {
+    field_id(::std::string::String),
+}
+
 impl EditFieldPayload {
     pub fn new() -> EditFieldPayload {
         ::std::default::Default::default()
@@ -1692,26 +1698,49 @@ impl EditFieldPayload {
 
 
     pub fn get_field_id(&self) -> &str {
-        &self.field_id
+        match self.one_of_field_id {
+            ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(ref v)) => v,
+            _ => "",
+        }
     }
     pub fn clear_field_id(&mut self) {
-        self.field_id.clear();
+        self.one_of_field_id = ::std::option::Option::None;
+    }
+
+    pub fn has_field_id(&self) -> bool {
+        match self.one_of_field_id {
+            ::std::option::Option::Some(EditFieldPayload_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.field_id = v;
+        self.one_of_field_id = ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::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
+        if let ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(_)) = self.one_of_field_id {
+        } else {
+            self.one_of_field_id = ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(::std::string::String::new()));
+        }
+        match self.one_of_field_id {
+            ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(ref mut v)) => v,
+            _ => panic!(),
+        }
     }
 
     // Take field
     pub fn take_field_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+        if self.has_field_id() {
+            match self.one_of_field_id.take() {
+                ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
     }
 
     // .FieldType field_type = 3;
@@ -1743,7 +1772,10 @@ impl ::protobuf::Message for EditFieldPayload {
                     ::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)?;
+                    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(EditFieldPayload_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)?
@@ -1763,12 +1795,16 @@ impl ::protobuf::Message for EditFieldPayload {
         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 != FieldType::RichText {
             my_size += ::protobuf::rt::enum_size(3, self.field_type);
         }
+        if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
+            match v {
+                &EditFieldPayload_oneof_one_of_field_id::field_id(ref v) => {
+                    my_size += ::protobuf::rt::string_size(2, &v);
+                },
+            };
+        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -1778,12 +1814,16 @@ impl ::protobuf::Message for EditFieldPayload {
         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 != FieldType::RichText {
             os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.field_type))?;
         }
+        if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
+            match v {
+                &EditFieldPayload_oneof_one_of_field_id::field_id(ref v) => {
+                    os.write_string(2, v)?;
+                },
+            };
+        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -1827,10 +1867,10 @@ impl ::protobuf::Message for EditFieldPayload {
                 |m: &EditFieldPayload| { &m.grid_id },
                 |m: &mut EditFieldPayload| { &mut m.grid_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "field_id",
-                |m: &EditFieldPayload| { &m.field_id },
-                |m: &mut EditFieldPayload| { &mut m.field_id },
+                EditFieldPayload::has_field_id,
+                EditFieldPayload::get_field_id,
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<FieldType>>(
                 "field_type",
@@ -1854,7 +1894,7 @@ impl ::protobuf::Message for EditFieldPayload {
 impl ::protobuf::Clear for EditFieldPayload {
     fn clear(&mut self) {
         self.grid_id.clear();
-        self.field_id.clear();
+        self.one_of_field_id = ::std::option::Option::None;
         self.field_type = FieldType::RichText;
         self.unknown_fields.clear();
     }
@@ -7928,78 +7968,79 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x05index\x18\x02\x20\x01(\x05R\x05index\"\x90\x01\n\x1aGetEditFieldCont\
     extPayload\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\x10EditFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridI\
-    d\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fieldId\x12)\n\nfield_typ\
-    e\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\"Z\n\x13FieldTypeOptionData\x12\
-    \x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12(\n\x10type_option_\
-    data\x18\x02\x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\x12\x1c\
-    \n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12RepeatedFi\
-    eldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05items\
-    \"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\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.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\"U\n\x0eGridBlockOrder\
-    \x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12(\n\nrow_orders\
-    \x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\"_\n\rIndexRowOrder\x12&\n\
-    \trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x05i\
-    ndex\x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone_of_index\"Q\n\x0fUp\
-    datedRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08row\
-    Order\x12\x16\n\x03row\x18\x02\x20\x01(\x0b2\x04.RowR\x03row\"\xc6\x01\n\
-    \x11GridRowsChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07block\
-    Id\x123\n\rinserted_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cins\
-    ertedRows\x12,\n\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bde\
-    letedRows\x123\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\x10.UpdatedRowOrd\
-    erR\x0bupdatedRows\"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(\x0b2\x05.CellR\x05items\"'\n\x11CreateG\
-    ridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06Grid\
-    Id\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\
-    \x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayloa\
-    d\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_ro\
-    w_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\
-    \xb6\x01\n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\t\
-    R\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\x15on\
-    e_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_orde\
-    rs\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\
-    \x15FieldChangesetPayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07f\
-    ieldId\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04n\
-    ame\x18\x03\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\
-    \x01R\x04desc\x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\
-    \tfieldType\x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\
-    \x20\n\nvisibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05w\
-    idth\x18\x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_option_data\x18\
-    \t\x20\x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_\
-    of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_\
-    of_visibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\
-    \x9c\x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
-    \x06gridId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\
-    \nfrom_index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\
-    \x04\x20\x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.Mo\
-    veItemTypeR\x02ty\"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\
-    \x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\
-    \x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\
-    \x18\x04\x20\x01(\tH\0R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\
-    \x12\r\n\tMoveField\x10\0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\
-    \x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08Date\
-    Time\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\
-    \x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\
+    \x03\x20\x01(\x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fone_of_field_id\"\
+    \x86\x01\n\x10EditFieldPayload\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\x0fo\
+    ne_of_field_id\"|\n\x10EditFieldContext\x12\x17\n\x07grid_id\x18\x01\x20\
+    \x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\x06.FieldR\t\
+    gridField\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOption\
+    Data\"Z\n\x13FieldTypeOptionData\x12\x19\n\x08field_id\x18\x01\x20\x01(\
+    \tR\x07fieldId\x12(\n\x10type_option_data\x18\x02\x20\x01(\x0cR\x0etypeO\
+    ptionData\"-\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\x08RowOrder\x12\x15\n\x06ro\
+    w_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_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_b\
+    y_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFiel\
+    dId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellBy\
+    FieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05va\
+    lue\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedR\
+    ow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Re\
+    peatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\
+    \x05items\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\
+    \tR\x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\
+    Orders\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.Row\
+    OrderR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\
+    \x0e\n\x0cone_of_index\"Q\n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\
+    \x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\
+    \x0b2\x04.RowR\x03row\"\xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08bloc\
+    k_id\x18\x01\x20\x01(\tR\x07blockId\x123\n\rinserted_rows\x18\x02\x20\
+    \x03(\x0b2\x0e.IndexRowOrderR\x0cinsertedRows\x12,\n\x0cdeleted_rows\x18\
+    \x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\
+    \x04\x20\x03(\x0b2\x10.UpdatedRowOrderR\x0bupdatedRows\"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\x12InsertFieldPayload\
+    \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\"\xa8\x03\n\x15FieldChangesetPayload\
+    \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\
+    id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\x01(\tH\0\
+    R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfie\
+    ld_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\x12\x18\n\x06\
+    frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvisibility\x18\
+    \x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\x08\x20\x01(\
+    \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\
+    \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\
+    _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\
+    \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\
+    emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\
+    \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\
+    \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\
+    \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\
+    \"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06grid\
+    Id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_i\
+    d\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0\
+    R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\
+    \0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\
+    \0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0c\
+    SingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Check\
+    box\x10\x05b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -35,7 +35,7 @@ message GetEditFieldContextPayload {
 }
 message EditFieldPayload {
     string grid_id = 1;
-    string field_id = 2;
+    oneof one_of_field_id { string field_id = 2; };
     FieldType field_type = 3;
 }
 message EditFieldContext {