Explorar o código

chore: read single select data from board

appflowy %!s(int64=2) %!d(string=hai) anos
pai
achega
2b745bc41a
Modificáronse 24 ficheiros con 430 adicións e 338 borrados
  1. 23 20
      frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
  2. 0 1
      frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
  3. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart
  4. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart
  5. 3 2
      frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart
  6. 0 154
      frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart
  7. 8 6
      frontend/app_flowy/lib/plugins/grid/application/field/field_type_option_edit_bloc.dart
  8. 3 2
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/date_bloc.dart
  9. 6 6
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/multi_select_type_option.dart
  10. 2 2
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/number_bloc.dart
  11. 19 10
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart
  12. 5 5
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/single_select_type_option.dart
  13. 223 0
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart
  14. 2 83
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_service.dart
  15. 1 0
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart
  16. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
  17. 2 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
  18. 1 0
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
  19. 121 35
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
  20. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checkbox.dart
  21. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/rich_text.dart
  22. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/url.dart
  23. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
  24. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

+ 23 - 20
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
 import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
+import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/builder.dart';
 import 'package:appflowy_board/appflowy_board.dart';
 import 'package:dartz/dartz.dart';
 import 'package:equatable/equatable.dart';
@@ -101,31 +102,33 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   }
 
   void _buildColumns(UnmodifiableListView<GridFieldPB> fields) {
-    List<BoardColumnData> columns = [];
-
     for (final field in fields) {
       if (field.fieldType == FieldType.SingleSelect) {
-        //  return BoardColumnData(customData: field, id: field.id, desc: "1");
+        _buildColumnsFromSingleSelect(field);
       }
     }
+  }
+
+  void _buildColumnsFromSingleSelect(GridFieldPB field) {
+    final typeOptionContext = makeTypeOptionContext<SingleSelectTypeOptionPB>(
+      gridId: _gridDataController.gridId,
+      field: field,
+    );
 
-    boardDataController.addColumns(columns);
-
-    // final column1 = BoardColumnData(id: "To Do", items: [
-    //   TextItem("Card 1"),
-    //   TextItem("Card 2"),
-    //   RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
-    //   TextItem("Card 4"),
-    // ]);
-    // final column2 = BoardColumnData(id: "In Progress", items: [
-    //   RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'),
-    //   TextItem("Card 6"),
-    // ]);
-
-    // final column3 = BoardColumnData(id: "Done", items: []);
-    // boardDataController.addColumn(column1);
-    // boardDataController.addColumn(column2);
-    // boardDataController.addColumn(column3);
+    typeOptionContext.loadTypeOptionData(
+      onCompleted: (singleSelect) {
+        List<BoardColumnData> columns = singleSelect.options.map((option) {
+          return BoardColumnData(
+            id: option.id,
+            desc: option.name,
+            customData: option,
+          );
+        }).toList();
+
+        boardDataController.addColumns(columns);
+      },
+      onError: (err) {},
+    );
   }
 
   Future<void> _loadGrid(Emitter<BoardState> emit) async {

+ 0 - 1
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -3,7 +3,6 @@
 import 'package:appflowy_board/appflowy_board.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import '../application/board_bloc.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart

@@ -17,7 +17,7 @@ import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'dart:convert' show utf8;
 
 import '../../field/field_cache.dart';
-import '../../field/type_option/type_option_service.dart';
+import '../../field/type_option/type_option_data_controller.dart';
 import 'cell_field_notifier.dart';
 part 'cell_service.freezed.dart';
 part 'cell_data_loader.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart

@@ -15,7 +15,7 @@ class SelectOptionService {
   String get rowId => cellId.rowId;
 
   Future<Either<Unit, FlowyError>> create({required String name}) {
-    return TypeOptionService(gridId: gridId, fieldId: fieldId)
+    return TypeOptionFFIService(gridId: gridId, fieldId: fieldId)
         .newOption(name: name)
         .then(
       (result) {

+ 3 - 2
frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart

@@ -1,9 +1,10 @@
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import 'field_service.dart';
 import 'package:dartz/dartz.dart';
+import 'type_option/type_option_data_controller.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
 part 'field_editor_bloc.freezed.dart';
 
 class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {

+ 0 - 154
frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart

@@ -141,157 +141,3 @@ class GridFieldCellContext with _$GridFieldCellContext {
     required GridFieldPB field,
   }) = _GridFieldCellContext;
 }
-
-abstract class IFieldTypeOptionLoader {
-  String get gridId;
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load();
-
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> switchToField(
-      String fieldId, FieldType fieldType) {
-    final payload = EditFieldPayloadPB.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..fieldType = fieldType;
-
-    return GridEventSwitchToField(payload).send();
-  }
-}
-
-class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
-  @override
-  final String gridId;
-  NewFieldTypeOptionLoader({
-    required this.gridId,
-  });
-
-  @override
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
-    final payload = CreateFieldPayloadPB.create()
-      ..gridId = gridId
-      ..fieldType = FieldType.RichText;
-
-    return GridEventCreateFieldTypeOption(payload).send();
-  }
-}
-
-class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
-  @override
-  final String gridId;
-  final GridFieldPB field;
-
-  FieldTypeOptionLoader({
-    required this.gridId,
-    required this.field,
-  });
-
-  @override
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
-    final payload = GridFieldTypeOptionIdPB.create()
-      ..gridId = gridId
-      ..fieldId = field.id
-      ..fieldType = field.fieldType;
-
-    return GridEventGetFieldTypeOption(payload).send();
-  }
-}
-
-class TypeOptionDataController {
-  final String gridId;
-  final IFieldTypeOptionLoader _loader;
-
-  late FieldTypeOptionDataPB _data;
-  final PublishNotifier<GridFieldPB> _fieldNotifier = PublishNotifier();
-
-  TypeOptionDataController({
-    required this.gridId,
-    required IFieldTypeOptionLoader loader,
-  }) : _loader = loader;
-
-  Future<Either<Unit, FlowyError>> loadTypeOptionData() async {
-    final result = await _loader.load();
-    return result.fold(
-      (data) {
-        data.freeze();
-        _data = data;
-        _fieldNotifier.value = data.field_2;
-        return left(unit);
-      },
-      (err) {
-        Log.error(err);
-        return right(err);
-      },
-    );
-  }
-
-  GridFieldPB get field => _data.field_2;
-
-  set field(GridFieldPB field) {
-    _updateData(newField: field);
-  }
-
-  List<int> get typeOptionData => _data.typeOptionData;
-
-  set fieldName(String name) {
-    _updateData(newName: name);
-  }
-
-  set typeOptionData(List<int> typeOptionData) {
-    _updateData(newTypeOptionData: typeOptionData);
-  }
-
-  void _updateData(
-      {String? newName, GridFieldPB? newField, List<int>? newTypeOptionData}) {
-    _data = _data.rebuild((rebuildData) {
-      if (newName != null) {
-        rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) {
-          rebuildField.name = newName;
-        });
-      }
-
-      if (newField != null) {
-        rebuildData.field_2 = newField;
-      }
-
-      if (newTypeOptionData != null) {
-        rebuildData.typeOptionData = newTypeOptionData;
-      }
-    });
-
-    _fieldNotifier.value = _data.field_2;
-
-    FieldService.insertField(
-      gridId: gridId,
-      field: field,
-      typeOptionData: typeOptionData,
-    );
-  }
-
-  Future<void> switchToField(FieldType newFieldType) {
-    return _loader.switchToField(field.id, newFieldType).then((result) {
-      return result.fold(
-        (fieldTypeOptionData) {
-          _updateData(
-            newField: fieldTypeOptionData.field_2,
-            newTypeOptionData: fieldTypeOptionData.typeOptionData,
-          );
-        },
-        (err) {
-          Log.error(err);
-        },
-      );
-    });
-  }
-
-  void Function() addFieldListener(void Function(GridFieldPB) callback) {
-    listener() {
-      callback(field);
-    }
-
-    _fieldNotifier.addListener(listener);
-    return listener;
-  }
-
-  void removeFieldListener(void Function() listener) {
-    _fieldNotifier.removeListener(listener);
-  }
-}

+ 8 - 6
frontend/app_flowy/lib/plugins/grid/application/field/field_type_option_edit_bloc.dart

@@ -2,12 +2,11 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-
-import 'field_service.dart';
-
+import 'type_option/type_option_data_controller.dart';
 part 'field_type_option_edit_bloc.freezed.dart';
 
-class FieldTypeOptionEditBloc extends Bloc<FieldTypeOptionEditEvent, FieldTypeOptionEditState> {
+class FieldTypeOptionEditBloc
+    extends Bloc<FieldTypeOptionEditEvent, FieldTypeOptionEditState> {
   final TypeOptionDataController _dataController;
   void Function()? _fieldListenFn;
 
@@ -42,7 +41,8 @@ class FieldTypeOptionEditBloc extends Bloc<FieldTypeOptionEditEvent, FieldTypeOp
 @freezed
 class FieldTypeOptionEditEvent with _$FieldTypeOptionEditEvent {
   const factory FieldTypeOptionEditEvent.initial() = _Initial;
-  const factory FieldTypeOptionEditEvent.didReceiveFieldUpdated(GridFieldPB field) = _DidReceiveFieldUpdated;
+  const factory FieldTypeOptionEditEvent.didReceiveFieldUpdated(
+      GridFieldPB field) = _DidReceiveFieldUpdated;
 }
 
 @freezed
@@ -51,7 +51,9 @@ class FieldTypeOptionEditState with _$FieldTypeOptionEditState {
     required GridFieldPB field,
   }) = _FieldTypeOptionEditState;
 
-  factory FieldTypeOptionEditState.initial(TypeOptionDataController fieldContext) => FieldTypeOptionEditState(
+  factory FieldTypeOptionEditState.initial(
+          TypeOptionDataController fieldContext) =>
+      FieldTypeOptionEditState(
         field: fieldContext.field,
       );
 }

+ 3 - 2
frontend/app_flowy/lib/plugins/grid/application/field/type_option/date_bloc.dart

@@ -1,13 +1,14 @@
-import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'package:protobuf/protobuf.dart';
+
+import 'type_option_data_controller.dart';
 part 'date_bloc.freezed.dart';
 
-typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>;
+typedef DateTypeOptionContext = TypeOptionContext<DateTypeOption>;
 
 class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
   @override

+ 6 - 6
frontend/app_flowy/lib/plugins/grid/application/field/type_option/multi_select_type_option.dart

@@ -1,21 +1,21 @@
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
 import 'dart:async';
-import 'package:protobuf/protobuf.dart';
 import 'select_option_type_option_bloc.dart';
+import 'type_option_data_controller.dart';
 import 'type_option_service.dart';
+import 'package:protobuf/protobuf.dart';
 
 class MultiSelectTypeOptionContext
-    extends TypeOptionWidgetContext<MultiSelectTypeOption>
+    extends TypeOptionContext<MultiSelectTypeOption>
     with SelectOptionTypeOptionAction {
-  final TypeOptionService service;
+  final TypeOptionFFIService service;
 
   MultiSelectTypeOptionContext({
     required MultiSelectTypeOptionWidgetDataParser dataParser,
     required TypeOptionDataController dataController,
-  })  : service = TypeOptionService(
+  })  : service = TypeOptionFFIService(
           gridId: dataController.gridId,
           fieldId: dataController.field.id,
         ),
@@ -59,7 +59,7 @@ class MultiSelectTypeOptionContext
   }
 
   @override
-  List<SelectOptionPB> Function(SelectOptionPB) get udpateOption {
+  List<SelectOptionPB> Function(SelectOptionPB) get updateOption {
     return (SelectOptionPB option) {
       typeOption.freeze();
       typeOption = typeOption.rebuild((typeOption) {

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/field/type_option/number_bloc.dart

@@ -1,14 +1,14 @@
-import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'package:protobuf/protobuf.dart';
+import 'type_option_data_controller.dart';
 
 part 'number_bloc.freezed.dart';
 
-typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
+typedef NumberTypeOptionContext = TypeOptionContext<NumberTypeOption>;
 
 class NumberTypeOptionWidgetDataParser
     extends TypeOptionDataParser<NumberTypeOption> {

+ 19 - 10
frontend/app_flowy/lib/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart

@@ -10,10 +10,11 @@ abstract class SelectOptionTypeOptionAction {
 
   List<SelectOptionPB> Function(SelectOptionPB) get deleteOption;
 
-  List<SelectOptionPB> Function(SelectOptionPB) get udpateOption;
+  List<SelectOptionPB> Function(SelectOptionPB) get updateOption;
 }
 
-class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTypeOptionState> {
+class SelectOptionTypeOptionBloc
+    extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTypeOptionState> {
   final SelectOptionTypeOptionAction typeOptionAction;
 
   SelectOptionTypeOptionBloc({
@@ -24,7 +25,8 @@ class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, Selec
       (event, emit) async {
         await event.when(
           createOption: (optionName) async {
-            final List<SelectOptionPB> options = await typeOptionAction.insertOption(optionName);
+            final List<SelectOptionPB> options =
+                await typeOptionAction.insertOption(optionName);
             emit(state.copyWith(options: options));
           },
           addingOption: () {
@@ -34,11 +36,13 @@ class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, Selec
             emit(state.copyWith(isEditingOption: false, newOptionName: none()));
           },
           updateOption: (option) {
-            final List<SelectOptionPB> options = typeOptionAction.udpateOption(option);
+            final List<SelectOptionPB> options =
+                typeOptionAction.updateOption(option);
             emit(state.copyWith(options: options));
           },
           deleteOption: (option) {
-            final List<SelectOptionPB> options = typeOptionAction.deleteOption(option);
+            final List<SelectOptionPB> options =
+                typeOptionAction.deleteOption(option);
             emit(state.copyWith(options: options));
           },
         );
@@ -54,11 +58,15 @@ class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, Selec
 
 @freezed
 class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent {
-  const factory SelectOptionTypeOptionEvent.createOption(String optionName) = _CreateOption;
+  const factory SelectOptionTypeOptionEvent.createOption(String optionName) =
+      _CreateOption;
   const factory SelectOptionTypeOptionEvent.addingOption() = _AddingOption;
-  const factory SelectOptionTypeOptionEvent.endAddingOption() = _EndAddingOption;
-  const factory SelectOptionTypeOptionEvent.updateOption(SelectOptionPB option) = _UpdateOption;
-  const factory SelectOptionTypeOptionEvent.deleteOption(SelectOptionPB option) = _DeleteOption;
+  const factory SelectOptionTypeOptionEvent.endAddingOption() =
+      _EndAddingOption;
+  const factory SelectOptionTypeOptionEvent.updateOption(
+      SelectOptionPB option) = _UpdateOption;
+  const factory SelectOptionTypeOptionEvent.deleteOption(
+      SelectOptionPB option) = _DeleteOption;
 }
 
 @freezed
@@ -69,7 +77,8 @@ class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState {
     required Option<String> newOptionName,
   }) = _SelectOptionTyepOptionState;
 
-  factory SelectOptionTypeOptionState.initial(List<SelectOptionPB> options) => SelectOptionTypeOptionState(
+  factory SelectOptionTypeOptionState.initial(List<SelectOptionPB> options) =>
+      SelectOptionTypeOptionState(
         options: options,
         isEditingOption: false,
         newOptionName: none(),

+ 5 - 5
frontend/app_flowy/lib/plugins/grid/application/field/type_option/single_select_type_option.dart

@@ -1,21 +1,21 @@
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart';
 import 'dart:async';
 import 'package:protobuf/protobuf.dart';
 import 'select_option_type_option_bloc.dart';
+import 'type_option_data_controller.dart';
 import 'type_option_service.dart';
 
 class SingleSelectTypeOptionContext
-    extends TypeOptionWidgetContext<SingleSelectTypeOptionPB>
+    extends TypeOptionContext<SingleSelectTypeOptionPB>
     with SelectOptionTypeOptionAction {
-  final TypeOptionService service;
+  final TypeOptionFFIService service;
 
   SingleSelectTypeOptionContext({
     required SingleSelectTypeOptionWidgetDataParser dataBuilder,
     required TypeOptionDataController dataController,
-  })  : service = TypeOptionService(
+  })  : service = TypeOptionFFIService(
           gridId: dataController.gridId,
           fieldId: dataController.field.id,
         ),
@@ -59,7 +59,7 @@ class SingleSelectTypeOptionContext
   }
 
   @override
-  List<SelectOptionPB> Function(SelectOptionPB) get udpateOption {
+  List<SelectOptionPB> Function(SelectOptionPB) get updateOption {
     return (SelectOptionPB option) {
       typeOption.freeze();
       typeOption = typeOption.rebuild((typeOption) {

+ 223 - 0
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart

@@ -0,0 +1,223 @@
+import 'package:flowy_infra/notifier.dart';
+import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
+import 'package:dartz/dartz.dart';
+import 'package:protobuf/protobuf.dart';
+import 'package:flowy_sdk/log.dart';
+
+abstract class TypeOptionDataParser<T> {
+  T fromBuffer(List<int> buffer);
+}
+
+class TypeOptionContext<T extends GeneratedMessage> {
+  T? _typeOptionObject;
+  final TypeOptionDataParser<T> dataParser;
+  final TypeOptionDataController _dataController;
+
+  TypeOptionContext({
+    required this.dataParser,
+    required TypeOptionDataController dataController,
+  }) : _dataController = dataController;
+
+  String get gridId => _dataController.gridId;
+
+  Future<void> loadTypeOptionData({
+    required void Function(T) onCompleted,
+    required void Function(FlowyError) onError,
+  }) async {
+    await _dataController.loadTypeOptionData().then((result) {
+      result.fold((l) => null, (err) => onError(err));
+    });
+
+    onCompleted(typeOption);
+  }
+
+  T get typeOption {
+    if (_typeOptionObject != null) {
+      return _typeOptionObject!;
+    }
+
+    final T object = _dataController.getTypeOption(dataParser);
+    _typeOptionObject = object;
+    return object;
+  }
+
+  set typeOption(T typeOption) {
+    _dataController.typeOptionData = typeOption.writeToBuffer();
+    _typeOptionObject = typeOption;
+  }
+}
+
+abstract class TypeOptionFieldDelegate {
+  void onFieldChanged(void Function(String) callback);
+  void dispose();
+}
+
+abstract class IFieldTypeOptionLoader {
+  String get gridId;
+  Future<Either<FieldTypeOptionDataPB, FlowyError>> load();
+
+  Future<Either<FieldTypeOptionDataPB, FlowyError>> switchToField(
+      String fieldId, FieldType fieldType) {
+    final payload = EditFieldPayloadPB.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..fieldType = fieldType;
+
+    return GridEventSwitchToField(payload).send();
+  }
+}
+
+class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
+  @override
+  final String gridId;
+  NewFieldTypeOptionLoader({
+    required this.gridId,
+  });
+
+  @override
+  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
+    final payload = CreateFieldPayloadPB.create()
+      ..gridId = gridId
+      ..fieldType = FieldType.RichText;
+
+    return GridEventCreateFieldTypeOption(payload).send();
+  }
+}
+
+class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
+  @override
+  final String gridId;
+  final GridFieldPB field;
+
+  FieldTypeOptionLoader({
+    required this.gridId,
+    required this.field,
+  });
+
+  @override
+  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
+    final payload = GridFieldTypeOptionIdPB.create()
+      ..gridId = gridId
+      ..fieldId = field.id
+      ..fieldType = field.fieldType;
+
+    return GridEventGetFieldTypeOption(payload).send();
+  }
+}
+
+class TypeOptionDataController {
+  final String gridId;
+  final IFieldTypeOptionLoader loader;
+  late FieldTypeOptionDataPB _data;
+  final PublishNotifier<GridFieldPB> _fieldNotifier = PublishNotifier();
+
+  TypeOptionDataController({
+    required this.gridId,
+    required this.loader,
+    GridFieldPB? field,
+  }) {
+    if (field != null) {
+      _data = FieldTypeOptionDataPB.create()
+        ..gridId = gridId
+        ..field_2 = field;
+    }
+  }
+
+  Future<Either<Unit, FlowyError>> loadTypeOptionData() async {
+    final result = await loader.load();
+    return result.fold(
+      (data) {
+        data.freeze();
+        _data = data;
+        _fieldNotifier.value = data.field_2;
+        return left(unit);
+      },
+      (err) {
+        Log.error(err);
+        return right(err);
+      },
+    );
+  }
+
+  GridFieldPB get field {
+    return _data.field_2;
+  }
+
+  set field(GridFieldPB field) {
+    _updateData(newField: field);
+  }
+
+  T getTypeOption<T>(TypeOptionDataParser<T> parser) {
+    return parser.fromBuffer(_data.typeOptionData);
+  }
+
+  set fieldName(String name) {
+    _updateData(newName: name);
+  }
+
+  set typeOptionData(List<int> typeOptionData) {
+    _updateData(newTypeOptionData: typeOptionData);
+  }
+
+  void _updateData({
+    String? newName,
+    GridFieldPB? newField,
+    List<int>? newTypeOptionData,
+  }) {
+    _data = _data.rebuild((rebuildData) {
+      if (newName != null) {
+        rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) {
+          rebuildField.name = newName;
+        });
+      }
+
+      if (newField != null) {
+        rebuildData.field_2 = newField;
+      }
+
+      if (newTypeOptionData != null) {
+        rebuildData.typeOptionData = newTypeOptionData;
+      }
+    });
+
+    _fieldNotifier.value = _data.field_2;
+
+    FieldService.insertField(
+      gridId: gridId,
+      field: field,
+      typeOptionData: _data.typeOptionData,
+    );
+  }
+
+  Future<void> switchToField(FieldType newFieldType) {
+    return loader.switchToField(field.id, newFieldType).then((result) {
+      return result.fold(
+        (fieldTypeOptionData) {
+          _updateData(
+            newField: fieldTypeOptionData.field_2,
+            newTypeOptionData: fieldTypeOptionData.typeOptionData,
+          );
+        },
+        (err) {
+          Log.error(err);
+        },
+      );
+    });
+  }
+
+  void Function() addFieldListener(void Function(GridFieldPB) callback) {
+    listener() {
+      callback(field);
+    }
+
+    _fieldNotifier.addListener(listener);
+    return listener;
+  }
+
+  void removeFieldListener(void Function() listener) {
+    _fieldNotifier.removeListener(listener);
+  }
+}

+ 2 - 83
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_service.dart

@@ -1,19 +1,14 @@
-import 'dart:typed_data';
-
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
-import 'package:protobuf/protobuf.dart';
 
-class TypeOptionService {
+class TypeOptionFFIService {
   final String gridId;
   final String fieldId;
 
-  TypeOptionService({
+  TypeOptionFFIService({
     required this.gridId,
     required this.fieldId,
   });
@@ -29,79 +24,3 @@ class TypeOptionService {
     return GridEventNewSelectOption(payload).send();
   }
 }
-
-abstract class TypeOptionDataParser<T> {
-  T fromBuffer(List<int> buffer);
-}
-
-class TypeOptionWidgetContext<T extends GeneratedMessage> {
-  T? _typeOptionObject;
-  final TypeOptionDataController _dataController;
-  final TypeOptionDataParser<T> dataParser;
-
-  TypeOptionWidgetContext({
-    required this.dataParser,
-    required TypeOptionDataController dataController,
-  }) : _dataController = dataController;
-
-  String get gridId => _dataController.gridId;
-
-  GridFieldPB get field => _dataController.field;
-
-  T get typeOption {
-    if (_typeOptionObject != null) {
-      return _typeOptionObject!;
-    }
-
-    final T object = dataParser.fromBuffer(_dataController.typeOptionData);
-    _typeOptionObject = object;
-    return object;
-  }
-
-  set typeOption(T typeOption) {
-    _dataController.typeOptionData = typeOption.writeToBuffer();
-    _typeOptionObject = typeOption;
-  }
-}
-
-abstract class TypeOptionFieldDelegate {
-  void onFieldChanged(void Function(String) callback);
-  void dispose();
-}
-
-class TypeOptionContext2<T> {
-  final String gridId;
-  final GridFieldPB field;
-  final FieldService _fieldService;
-  T? _data;
-  final TypeOptionDataParser dataBuilder;
-
-  TypeOptionContext2({
-    required this.gridId,
-    required this.field,
-    required this.dataBuilder,
-    Uint8List? data,
-  }) : _fieldService = FieldService(gridId: gridId, fieldId: field.id) {
-    if (data != null) {
-      _data = dataBuilder.fromBuffer(data);
-    }
-  }
-
-  Future<Either<T, FlowyError>> typeOptionData() {
-    if (_data != null) {
-      return Future(() => left(_data!));
-    }
-
-    return _fieldService
-        .getFieldTypeOptionData(fieldType: field.fieldType)
-        .then((result) {
-      return result.fold(
-        (data) {
-          _data = dataBuilder.fromBuffer(data.typeOptionData);
-          return left(_data!);
-        },
-        (err) => right(err),
-      );
-    });
-  }
-}

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart

@@ -1,5 +1,6 @@
 import 'package:app_flowy/plugins/grid/application/field/field_cell_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';

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

@@ -1,5 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';

+ 2 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart

@@ -1,4 +1,5 @@
 import 'dart:typed_data';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:dartz/dartz.dart' show Either;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -94,8 +95,8 @@ class _FieldTypeOptionEditorState extends State<FieldTypeOptionEditor> {
 
     return makeTypeOptionWidget(
       context: context,
-      dataController: widget.dataController,
       overlayDelegate: overlayDelegate,
+      dataController: widget.dataController,
     );
   }
 

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart

@@ -1,4 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:flowy_infra/image.dart';

+ 121 - 35
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart

@@ -1,6 +1,15 @@
 import 'dart:typed_data';
 
 import 'package:app_flowy/plugins/grid/application/field/type_option/multi_select_type_option.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
+import 'package:protobuf/protobuf.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
@@ -39,70 +48,147 @@ Widget? makeTypeOptionWidget({
   required TypeOptionDataController dataController,
   required TypeOptionOverlayDelegate overlayDelegate,
 }) {
-  final builder = makeTypeOptionWidgetBuilder(dataController, overlayDelegate);
+  final builder = makeTypeOptionWidgetBuilder(
+    dataController: dataController,
+    overlayDelegate: overlayDelegate,
+  );
   return builder.build(context);
 }
 
-TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
-  TypeOptionDataController dataController,
-  TypeOptionOverlayDelegate overlayDelegate,
-) {
+TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({
+  required TypeOptionDataController dataController,
+  required TypeOptionOverlayDelegate overlayDelegate,
+}) {
+  final gridId = dataController.gridId;
+  final fieldType = dataController.field.fieldType;
+
   switch (dataController.field.fieldType) {
     case FieldType.Checkbox:
-      final context = CheckboxTypeOptionContext(
-        dataController: dataController,
-        dataParser: CheckboxTypeOptionWidgetDataParser(),
+      return CheckboxTypeOptionWidgetBuilder(
+        makeTypeOptionContextWithDataController<CheckboxTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ),
       );
-      return CheckboxTypeOptionWidgetBuilder(context);
     case FieldType.DateTime:
-      final context = DateTypeOptionContext(
-        dataController: dataController,
-        dataParser: DateTypeOptionDataParser(),
-      );
       return DateTypeOptionWidgetBuilder(
-        context,
+        makeTypeOptionContextWithDataController<DateTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ),
         overlayDelegate,
       );
     case FieldType.SingleSelect:
-      final context = SingleSelectTypeOptionContext(
-        dataController: dataController,
-        dataBuilder: SingleSelectTypeOptionWidgetDataParser(),
-      );
       return SingleSelectTypeOptionWidgetBuilder(
-        context,
+        makeTypeOptionContextWithDataController<SingleSelectTypeOptionPB>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ) as SingleSelectTypeOptionContext,
         overlayDelegate,
       );
     case FieldType.MultiSelect:
-      final context = MultiSelectTypeOptionContext(
-        dataController: dataController,
-        dataParser: MultiSelectTypeOptionWidgetDataParser(),
-      );
       return MultiSelectTypeOptionWidgetBuilder(
-        context,
+        makeTypeOptionContextWithDataController<MultiSelectTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ) as MultiSelectTypeOptionContext,
         overlayDelegate,
       );
     case FieldType.Number:
-      final context = NumberTypeOptionContext(
-        dataController: dataController,
-        dataParser: NumberTypeOptionWidgetDataParser(),
-      );
       return NumberTypeOptionWidgetBuilder(
-        context,
+        makeTypeOptionContextWithDataController<NumberTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ),
         overlayDelegate,
       );
     case FieldType.RichText:
-      final context = RichTextTypeOptionContext(
+      return RichTextTypeOptionWidgetBuilder(
+        makeTypeOptionContextWithDataController<RichTextTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ),
+      );
+
+    case FieldType.URL:
+      return URLTypeOptionWidgetBuilder(
+        makeTypeOptionContextWithDataController<URLTypeOption>(
+          gridId: gridId,
+          fieldType: fieldType,
+          dataController: dataController,
+        ),
+      );
+  }
+  throw UnimplementedError;
+}
+
+TypeOptionContext<T> makeTypeOptionContext<T extends GeneratedMessage>({
+  required String gridId,
+  required GridFieldPB field,
+}) {
+  final loader = FieldTypeOptionLoader(gridId: gridId, field: field);
+  final dataController = TypeOptionDataController(
+    gridId: gridId,
+    loader: loader,
+    field: field,
+  );
+  return makeTypeOptionContextWithDataController(
+    gridId: gridId,
+    fieldType: field.fieldType,
+    dataController: dataController,
+  );
+}
+
+TypeOptionContext<T>
+    makeTypeOptionContextWithDataController<T extends GeneratedMessage>({
+  required String gridId,
+  required FieldType fieldType,
+  required TypeOptionDataController dataController,
+}) {
+  switch (fieldType) {
+    case FieldType.Checkbox:
+      return CheckboxTypeOptionContext(
+        dataController: dataController,
+        dataParser: CheckboxTypeOptionWidgetDataParser(),
+      ) as TypeOptionContext<T>;
+    case FieldType.DateTime:
+      return DateTypeOptionContext(
+        dataController: dataController,
+        dataParser: DateTypeOptionDataParser(),
+      ) as TypeOptionContext<T>;
+    case FieldType.SingleSelect:
+      return SingleSelectTypeOptionContext(
+        dataController: dataController,
+        dataBuilder: SingleSelectTypeOptionWidgetDataParser(),
+      ) as TypeOptionContext<T>;
+    case FieldType.MultiSelect:
+      return MultiSelectTypeOptionContext(
+        dataController: dataController,
+        dataParser: MultiSelectTypeOptionWidgetDataParser(),
+      ) as TypeOptionContext<T>;
+    case FieldType.Number:
+      return NumberTypeOptionContext(
+        dataController: dataController,
+        dataParser: NumberTypeOptionWidgetDataParser(),
+      ) as TypeOptionContext<T>;
+    case FieldType.RichText:
+      return RichTextTypeOptionContext(
         dataController: dataController,
         dataParser: RichTextTypeOptionWidgetDataParser(),
-      );
-      return RichTextTypeOptionWidgetBuilder(context);
+      ) as TypeOptionContext<T>;
 
     case FieldType.URL:
-      final context = URLTypeOptionContext(
+      return URLTypeOptionContext(
         dataController: dataController,
         dataParser: URLTypeOptionWidgetDataParser(),
-      );
-      return URLTypeOptionWidgetBuilder(context);
+      ) as TypeOptionContext<T>;
   }
+
   throw UnimplementedError;
 }

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checkbox.dart

@@ -1,9 +1,9 @@
-import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'builder.dart';
 
-typedef CheckboxTypeOptionContext = TypeOptionWidgetContext<CheckboxTypeOption>;
+typedef CheckboxTypeOptionContext = TypeOptionContext<CheckboxTypeOption>;
 
 class CheckboxTypeOptionWidgetDataParser
     extends TypeOptionDataParser<CheckboxTypeOption> {

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/rich_text.dart

@@ -1,9 +1,9 @@
-import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'builder.dart';
 
-typedef RichTextTypeOptionContext = TypeOptionWidgetContext<RichTextTypeOption>;
+typedef RichTextTypeOptionContext = TypeOptionContext<RichTextTypeOption>;
 
 class RichTextTypeOptionWidgetDataParser
     extends TypeOptionDataParser<RichTextTypeOption> {

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/url.dart

@@ -1,9 +1,9 @@
-import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'builder.dart';
 
-typedef URLTypeOptionContext = TypeOptionWidgetContext<URLTypeOption>;
+typedef URLTypeOptionContext = TypeOptionContext<URLTypeOption>;
 
 class URLTypeOptionWidgetDataParser
     extends TypeOptionDataParser<URLTypeOption> {

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart

@@ -1,5 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart';
 import 'package:flowy_infra/image.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

@@ -1,5 +1,5 @@
+import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:app_flowy/plugins/grid/application/setting/property_bloc.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
 import 'package:flowy_infra/image.dart';