Quellcode durchsuchen

chore: Merge branch 'main' into feat_grid_url

# Conflicts:
#	frontend/rust-lib/flowy-grid/src/event_handler.rs
#	shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
appflowy vor 2 Jahren
Ursprung
Commit
9844c02cbc
49 geänderte Dateien mit 610 neuen und 701 gelöschten Zeilen
  1. 1 0
      frontend/Makefile.toml
  2. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  3. 34 16
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart
  4. 59 37
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart
  5. 10 12
      frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart
  6. 6 7
      frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart
  7. 0 5
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart
  8. 38 5
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart
  9. 11 18
      frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart
  10. 0 17
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  11. 6 20
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  12. 2 3
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  13. 0 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  14. 1 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  15. 4 5
      frontend/rust-lib/flowy-folder/src/manager.rs
  16. 1 1
      frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs
  17. 3 3
      frontend/rust-lib/flowy-folder/src/services/folder_editor.rs
  18. 3 6
      frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs
  19. 2 2
      frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs
  20. 2 2
      frontend/rust-lib/flowy-folder/src/services/view/controller.rs
  21. 2 2
      frontend/rust-lib/flowy-folder/tests/workspace/script.rs
  22. 17 38
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  23. 0 4
      frontend/rust-lib/flowy-grid/src/event_map.rs
  24. 20 44
      frontend/rust-lib/flowy-grid/src/manager.rs
  25. 2 5
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  26. 0 1
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  27. 2 3
      frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs
  28. 18 18
      frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs
  29. 7 7
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  30. 64 119
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs
  31. 5 5
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs
  32. 73 74
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs
  33. 16 10
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  34. 27 27
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  35. 3 3
      frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs
  36. 38 18
      frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs
  37. 4 6
      frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs
  38. 4 3
      frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs
  39. 3 3
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  40. 4 4
      frontend/rust-lib/flowy-text-block/src/editor.rs
  41. 9 9
      frontend/rust-lib/flowy-text-block/src/manager.rs
  42. 2 2
      frontend/rust-lib/flowy-text-block/tests/document/script.rs
  43. 12 24
      frontend/scripts/makefile/tests.toml
  44. 27 0
      frontend/scripts/makefile/tool.toml
  45. 8 0
      shared-lib/flowy-derive/src/proto_buf/deserialize.rs
  46. 3 8
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  47. 54 97
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  48. 1 2
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto
  49. 1 1
      shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs

+ 1 - 0
frontend/Makefile.toml

@@ -7,6 +7,7 @@ extend = [
     { path = "scripts/makefile/docker.toml" },
     { path = "scripts/makefile/env.toml" },
     { path = "scripts/makefile/flutter.toml" },
+    { path = "scripts/makefile/tool.toml" },
 ]
 
 [config]

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

@@ -15,7 +15,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
 import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
-
+import 'dart:convert' show utf8;
 part 'cell_service.freezed.dart';
 part 'data_loader.dart';
 part 'context_builder.dart';

+ 34 - 16
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart

@@ -1,6 +1,6 @@
 part of 'cell_service.dart';
 
-typedef GridCellContext = _GridCellContext<Cell, String>;
+typedef GridCellContext = _GridCellContext<String, String>;
 typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
 typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>;
 typedef GridURLCellContext = _GridCellContext<Cell, String>;
@@ -17,26 +17,33 @@ class GridCellContextBuilder {
   _GridCellContext build() {
     switch (_gridCell.field.fieldType) {
       case FieldType.Checkbox:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          parser: StringCellDataParser(),
+        );
         return GridCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.DateTime:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          parser: DateCellDataParser(),
+        );
+
         return GridDateCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: DateCellDataLoader(gridCell: _gridCell),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.Number:
         final cellDataLoader = GridCellDataLoader(
           gridCell: _gridCell,
-          config: const GridCellDataConfig(
-            reloadOnCellChanged: true,
-            reloadOnFieldChanged: true,
-          ),
+          parser: StringCellDataParser(),
+          config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
         );
         return GridCellContext(
           gridCell: _gridCell,
@@ -45,26 +52,40 @@ class GridCellContextBuilder {
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.RichText:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          parser: StringCellDataParser(),
+        );
         return GridCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.MultiSelect:
       case FieldType.SingleSelect:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          parser: SelectOptionCellDataParser(),
+          config: const GridCellDataConfig(reloadOnFieldChanged: true),
+        );
+
         return GridSelectOptionCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: SelectOptionCellDataLoader(gridCell: _gridCell),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
 
       case FieldType.URL:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          parser: URLCellDataParser(),
+        );
         return GridURLCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
     }
@@ -141,10 +162,7 @@ class _GridCellContext<T, D> extends Equatable {
     }
 
     onCellChangedFn() {
-      final value = _cellDataNotifier.value;
-      if (value is T) {
-        onCellChanged(value);
-      }
+      onCellChanged(_cellDataNotifier.value as T);
 
       if (cellDataLoader.config.reloadOnCellChanged) {
         _loadData();
@@ -159,9 +177,9 @@ class _GridCellContext<T, D> extends Equatable {
     _cellDataNotifier.removeListener(fn);
   }
 
-  T? getCellData() {
+  T? getCellData({bool loadIfNoCache = true}) {
     final data = cellCache.get(_cacheKey);
-    if (data == null) {
+    if (data == null && loadIfNoCache) {
       _loadData();
     }
     return data;

+ 59 - 37
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart

@@ -4,8 +4,8 @@ abstract class IGridCellDataConfig {
   // The cell data will reload if it receives the field's change notification.
   bool get reloadOnFieldChanged;
 
-  // The cell data will reload if it receives the cell's change notification.
-  // For example, the number cell should be reloaded after user input the number.
+  // When the reloadOnCellChanged is true, it will load the cell data after user input.
+  // For example: The number cell reload the cell data that carries the format
   // user input: 12
   // cell display: $12
   bool get reloadOnCellChanged;
@@ -30,60 +30,45 @@ abstract class IGridCellDataLoader<T> {
   IGridCellDataConfig get config;
 }
 
-class GridCellDataLoader extends IGridCellDataLoader<Cell> {
+abstract class ICellDataParser<T> {
+  T? parserData(List<int> data);
+}
+
+class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
   final CellService service = CellService();
   final GridCell gridCell;
+  final ICellDataParser<T> parser;
 
   @override
   final IGridCellDataConfig config;
 
   GridCellDataLoader({
     required this.gridCell,
+    required this.parser,
     this.config = const GridCellDataConfig(),
   });
 
   @override
-  Future<Cell?> loadData() {
+  Future<T?> loadData() {
     final fut = service.getCell(
       gridId: gridCell.gridId,
       fieldId: gridCell.field.id,
       rowId: gridCell.rowId,
     );
-    return fut.then((result) {
-      return result.fold((data) => data, (err) {
+    return fut.then(
+      (result) => result.fold((Cell cell) {
+        try {
+          return parser.parserData(cell.data);
+        } catch (e, s) {
+          Log.error('$parser parser cellData failed, $e');
+          Log.error('Stack trace \n $s');
+          return null;
+        }
+      }, (err) {
         Log.error(err);
         return null;
-      });
-    });
-  }
-}
-
-class DateCellDataLoader extends IGridCellDataLoader<DateCellData> {
-  final GridCell gridCell;
-  final IGridCellDataConfig _config;
-  DateCellDataLoader({
-    required this.gridCell,
-  }) : _config = const GridCellDataConfig(reloadOnFieldChanged: true);
-
-  @override
-  IGridCellDataConfig get config => _config;
-
-  @override
-  Future<DateCellData?> loadData() {
-    final payload = CellIdentifierPayload.create()
-      ..gridId = gridCell.gridId
-      ..fieldId = gridCell.field.id
-      ..rowId = gridCell.rowId;
-
-    return GridEventGetDateCellData(payload).send().then((result) {
-      return result.fold(
-        (data) => data,
-        (err) {
-          Log.error(err);
-          return null;
-        },
-      );
-    });
+      }),
+    );
   }
 }
 
@@ -109,3 +94,40 @@ class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellDat
   @override
   IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
 }
+
+class StringCellDataParser implements ICellDataParser<String> {
+  @override
+  String? parserData(List<int> data) {
+    return utf8.decode(data);
+  }
+}
+
+class DateCellDataParser implements ICellDataParser<DateCellData> {
+  @override
+  DateCellData? parserData(List<int> data) {
+    if (data.isEmpty) {
+      return null;
+    }
+    return DateCellData.fromBuffer(data);
+  }
+}
+
+class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
+  @override
+  SelectOptionCellData? parserData(List<int> data) {
+    if (data.isEmpty) {
+      return null;
+    }
+    return SelectOptionCellData.fromBuffer(data);
+  }
+}
+
+class URLCellDataParser implements ICellDataParser<Cell> {
+  @override
+  Cell? parserData(List<int> data) {
+    if (data.isEmpty) {
+      return null;
+    }
+    return Cell.fromBuffer(data);
+  }
+}

+ 10 - 12
frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart

@@ -1,4 +1,3 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -16,15 +15,15 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
   }) : super(CheckboxCellState.initial(cellContext)) {
     on<CheckboxCellEvent>(
       (event, emit) async {
-        await event.map(
-          initial: (_Initial value) {
+        await event.when(
+          initial: () {
             _startListening();
           },
-          select: (_Selected value) async {
+          select: () async {
             _updateCellData();
           },
-          didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
-            emit(state.copyWith(isSelected: _isSelected(value.cell)));
+          didReceiveCellUpdate: (cellData) {
+            emit(state.copyWith(isSelected: _isSelected(cellData)));
           },
         );
       },
@@ -43,9 +42,9 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
   }
 
   void _startListening() {
-    _onCellChangedFn = cellContext.startListening(onCellChanged: ((cell) {
+    _onCellChangedFn = cellContext.startListening(onCellChanged: ((cellData) {
       if (!isClosed) {
-        add(CheckboxCellEvent.didReceiveCellUpdate(cell));
+        add(CheckboxCellEvent.didReceiveCellUpdate(cellData));
       }
     }));
   }
@@ -59,7 +58,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
 class CheckboxCellEvent with _$CheckboxCellEvent {
   const factory CheckboxCellEvent.initial() = _Initial;
   const factory CheckboxCellEvent.select() = _Selected;
-  const factory CheckboxCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
+  const factory CheckboxCellEvent.didReceiveCellUpdate(String cellData) = _DidReceiveCellUpdate;
 }
 
 @freezed
@@ -73,7 +72,6 @@ class CheckboxCellState with _$CheckboxCellState {
   }
 }
 
-bool _isSelected(Cell? cell) {
-  final content = cell?.content ?? "";
-  return content == "Yes";
+bool _isSelected(String? cellData) {
+  return cellData == "Yes";
 }

+ 6 - 7
frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart

@@ -1,4 +1,3 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -20,7 +19,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
             _startListening();
           },
           didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
-            emit(state.copyWith(content: value.cell.content));
+            emit(state.copyWith(content: value.cellContent));
           },
           updateCell: (_UpdateCell value) async {
             await _updateCellValue(value, emit);
@@ -46,9 +45,9 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
 
   void _startListening() {
     _onCellChangedFn = cellContext.startListening(
-      onCellChanged: ((cell) {
+      onCellChanged: ((cellContent) {
         if (!isClosed) {
-          add(NumberCellEvent.didReceiveCellUpdate(cell));
+          add(NumberCellEvent.didReceiveCellUpdate(cellContent));
         }
       }),
     );
@@ -59,7 +58,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
 class NumberCellEvent with _$NumberCellEvent {
   const factory NumberCellEvent.initial() = _Initial;
   const factory NumberCellEvent.updateCell(String text) = _UpdateCell;
-  const factory NumberCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
+  const factory NumberCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate;
 }
 
 @freezed
@@ -69,7 +68,7 @@ class NumberCellState with _$NumberCellState {
   }) = _NumberCellState;
 
   factory NumberCellState.initial(GridCellContext context) {
-    final cell = context.getCellData();
-    return NumberCellState(content: cell?.content ?? "");
+    final cellContent = context.getCellData() ?? "";
+    return NumberCellState(content: cellContent);
   }
 }

+ 0 - 5
frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart

@@ -21,7 +21,6 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
           },
           didReceiveOptions: (_DidReceiveOptions value) {
             emit(state.copyWith(
-              options: value.options,
               selectedOptions: value.selectedOptions,
             ));
           },
@@ -45,7 +44,6 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
       onCellChanged: ((selectOptionContext) {
         if (!isClosed) {
           add(SelectOptionCellEvent.didReceiveOptions(
-            selectOptionContext.options,
             selectOptionContext.selectOptions,
           ));
         }
@@ -58,7 +56,6 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
 class SelectOptionCellEvent with _$SelectOptionCellEvent {
   const factory SelectOptionCellEvent.initial() = _InitialCell;
   const factory SelectOptionCellEvent.didReceiveOptions(
-    List<SelectOption> options,
     List<SelectOption> selectedOptions,
   ) = _DidReceiveOptions;
 }
@@ -66,7 +63,6 @@ class SelectOptionCellEvent with _$SelectOptionCellEvent {
 @freezed
 class SelectOptionCellState with _$SelectOptionCellState {
   const factory SelectOptionCellState({
-    required List<SelectOption> options,
     required List<SelectOption> selectedOptions,
   }) = _SelectOptionCellState;
 
@@ -74,7 +70,6 @@ class SelectOptionCellState with _$SelectOptionCellState {
     final data = context.getCellData();
 
     return SelectOptionCellState(
-      options: data?.options ?? [],
       selectedOptions: data?.selectOptions ?? [],
     );
   }

+ 38 - 5
frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart

@@ -1,4 +1,5 @@
 import 'dart:async';
+import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
@@ -13,17 +14,21 @@ part 'select_option_editor_bloc.freezed.dart';
 class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
   final SelectOptionService _selectOptionService;
   final GridSelectOptionCellContext cellContext;
+  late final GridFieldsListener _fieldListener;
   void Function()? _onCellChangedFn;
+  Timer? _delayOperation;
 
   SelectOptionCellEditorBloc({
     required this.cellContext,
   })  : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell),
+        _fieldListener = GridFieldsListener(gridId: cellContext.gridId),
         super(SelectOptionEditorState.initial(cellContext)) {
     on<SelectOptionEditorEvent>(
       (event, emit) async {
         await event.map(
           initial: (_Initial value) async {
             _startListening();
+            _loadOptions();
           },
           didReceiveOptions: (_DidReceiveOptions value) {
             final result = _makeOptions(state.filter, value.options);
@@ -63,6 +68,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
       cellContext.removeListener(_onCellChangedFn!);
       _onCellChangedFn = null;
     }
+    _delayOperation?.cancel();
+    await _fieldListener.stop();
     cellContext.dispose();
     return super.close();
   }
@@ -106,6 +113,24 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
     ));
   }
 
+  void _loadOptions() {
+    _delayOperation?.cancel();
+    _delayOperation = Timer(const Duration(milliseconds: 10), () {
+      _selectOptionService.getOpitonContext().then((result) {
+        if (isClosed) {
+          return;
+        }
+        return result.fold(
+          (data) => add(SelectOptionEditorEvent.didReceiveOptions(data.options, data.selectOptions)),
+          (err) {
+            Log.error(err);
+            return null;
+          },
+        );
+      });
+    });
+  }
+
   _MakeOptionResult _makeOptions(Option<String> filter, List<SelectOption> allOptions) {
     final List<SelectOption> options = List.from(allOptions);
     Option<String> createOption = filter;
@@ -135,13 +160,21 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
     _onCellChangedFn = cellContext.startListening(
       onCellChanged: ((selectOptionContext) {
         if (!isClosed) {
-          add(SelectOptionEditorEvent.didReceiveOptions(
-            selectOptionContext.options,
-            selectOptionContext.selectOptions,
-          ));
+          _loadOptions();
         }
       }),
     );
+
+    _fieldListener.start(onFieldsChanged: (result) {
+      result.fold(
+        (changeset) {
+          if (changeset.updatedFields.isNotEmpty) {
+            _loadOptions();
+          }
+        },
+        (err) => Log.error(err),
+      );
+    });
   }
 }
 
@@ -168,7 +201,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
   }) = _SelectOptionEditorState;
 
   factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
-    final data = context.getCellData();
+    final data = context.getCellData(loadIfNoCache: false);
     return SelectOptionEditorState(
       options: data?.options ?? [],
       allOptions: data?.options ?? [],

+ 11 - 18
frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart

@@ -1,4 +1,3 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -14,21 +13,16 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
   }) : super(TextCellState.initial(cellContext)) {
     on<TextCellEvent>(
       (event, emit) async {
-        await event.map(
-          initial: (_InitialCell value) async {
+        await event.when(
+          initial: () async {
             _startListening();
           },
-          updateText: (_UpdateText value) {
-            cellContext.saveCellData(value.text);
-            emit(state.copyWith(content: value.text));
+          updateText: (text) {
+            cellContext.saveCellData(text);
+            emit(state.copyWith(content: text));
           },
-          didReceiveCellData: (_DidReceiveCellData value) {
-            emit(state.copyWith(content: value.cellData.cell?.content ?? ""));
-          },
-          didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
-            emit(state.copyWith(
-              content: value.cell.content,
-            ));
+          didReceiveCellUpdate: (content) {
+            emit(state.copyWith(content: content));
           },
         );
       },
@@ -47,9 +41,9 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
 
   void _startListening() {
     _onCellChangedFn = cellContext.startListening(
-      onCellChanged: ((cell) {
+      onCellChanged: ((cellContent) {
         if (!isClosed) {
-          add(TextCellEvent.didReceiveCellUpdate(cell));
+          add(TextCellEvent.didReceiveCellUpdate(cellContent));
         }
       }),
     );
@@ -59,8 +53,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
 @freezed
 class TextCellEvent with _$TextCellEvent {
   const factory TextCellEvent.initial() = _InitialCell;
-  const factory TextCellEvent.didReceiveCellData(GridCell cellData) = _DidReceiveCellData;
-  const factory TextCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
+  const factory TextCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate;
   const factory TextCellEvent.updateText(String text) = _UpdateText;
 }
 
@@ -71,6 +64,6 @@ class TextCellState with _$TextCellState {
   }) = _TextCellState;
 
   factory TextCellState.initial(GridCellContext context) => TextCellState(
-        content: context.getCellData()?.content ?? "",
+        content: context.getCellData() ?? "",
       );
 }

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

@@ -392,20 +392,3 @@ class GridEventUpdateDateCell {
     }
 }
 
-class GridEventGetDateCellData {
-     CellIdentifierPayload request;
-     GridEventGetDateCellData(this.request);
-
-    Future<Either<DateCellData, FlowyError>> send() {
-    final request = FFIRequest.create()
-          ..event = GridEvent.GetDateCellData.toString()
-          ..payload = requestToBytes(this.request);
-
-    return Dispatch.asyncRequest(request)
-        .then((bytesResult) => bytesResult.fold(
-           (okBytes) => left(DateCellData.fromBuffer(okBytes)),
-           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
-        ));
-    }
-}
-

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

@@ -1366,24 +1366,19 @@ class GridBlock extends $pb.GeneratedMessage {
 class Cell extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Cell', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
-    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content')
-    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data')
+    ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
     ..hasRequiredFields = false
   ;
 
   Cell._() : super();
   factory Cell({
     $core.String? fieldId,
-    $core.String? content,
-    $core.String? data,
+    $core.List<$core.int>? data,
   }) {
     final _result = create();
     if (fieldId != null) {
       _result.fieldId = fieldId;
     }
-    if (content != null) {
-      _result.content = content;
-    }
     if (data != null) {
       _result.data = data;
     }
@@ -1420,22 +1415,13 @@ class Cell extends $pb.GeneratedMessage {
   void clearFieldId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.String get content => $_getSZ(1);
+  $core.List<$core.int> get data => $_getN(1);
   @$pb.TagNumber(2)
-  set content($core.String v) { $_setString(1, v); }
+  set data($core.List<$core.int> v) { $_setBytes(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasContent() => $_has(1);
+  $core.bool hasData() => $_has(1);
   @$pb.TagNumber(2)
-  void clearContent() => clearField(2);
-
-  @$pb.TagNumber(3)
-  $core.String get data => $_getSZ(2);
-  @$pb.TagNumber(3)
-  set data($core.String v) { $_setString(2, v); }
-  @$pb.TagNumber(3)
-  $core.bool hasData() => $_has(2);
-  @$pb.TagNumber(3)
-  void clearData() => clearField(3);
+  void clearData() => clearField(2);
 }
 
 class RepeatedCell extends $pb.GeneratedMessage {

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

@@ -290,13 +290,12 @@ const Cell$json = const {
   '1': 'Cell',
   '2': const [
     const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
-    const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'},
-    const {'1': 'data', '3': 3, '4': 1, '5': 9, '10': 'data'},
+    const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
   ],
 };
 
 /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoCVIEZGF0YQ==');
+final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhIKBGRhdGEYAiABKAxSBGRhdGE=');
 @$core.Deprecated('Use repeatedCellDescriptor instead')
 const RepeatedCell$json = const {
   '1': 'RepeatedCell',

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

@@ -33,7 +33,6 @@ class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent UpdateCell = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell');
   static const GridEvent UpdateSelectOptionCell = GridEvent._(72, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOptionCell');
   static const GridEvent UpdateDateCell = GridEvent._(80, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateDateCell');
-  static const GridEvent GetDateCellData = GridEvent._(90, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetDateCellData');
 
   static const $core.List<GridEvent> values = <GridEvent> [
     GetGridData,
@@ -59,7 +58,6 @@ class GridEvent extends $pb.ProtobufEnum {
     UpdateCell,
     UpdateSelectOptionCell,
     UpdateDateCell,
-    GetDateCellData,
   ];
 
   static final $core.Map<$core.int, GridEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

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

@@ -35,9 +35,8 @@ const GridEvent$json = const {
     const {'1': 'UpdateCell', '2': 71},
     const {'1': 'UpdateSelectOptionCell', '2': 72},
     const {'1': 'UpdateDateCell', '2': 80},
-    const {'1': 'GetDateCellData', '2': 90},
   ],
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo=');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFA=');

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

@@ -4,7 +4,7 @@ use crate::{
     errors::FlowyResult,
     event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
     services::{
-        folder_editor::ClientFolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
+        folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
         TrashController, ViewController, WorkspaceController,
     },
 };
@@ -61,7 +61,7 @@ pub struct FolderManager {
     pub(crate) view_controller: Arc<ViewController>,
     pub(crate) trash_controller: Arc<TrashController>,
     web_socket: Arc<dyn RevisionWebSocket>,
-    folder_editor: Arc<TokioRwLock<Option<Arc<ClientFolderEditor>>>>,
+    folder_editor: Arc<TokioRwLock<Option<Arc<FolderEditor>>>>,
     data_processors: ViewDataProcessorMap,
 }
 
@@ -166,8 +166,7 @@ impl FolderManager {
         let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache));
         let rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence);
 
-        let folder_editor =
-            ClientFolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
+        let folder_editor = FolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
         *self.folder_editor.write().await = Some(Arc::new(folder_editor));
 
         let _ = self.app_controller.initialize()?;
@@ -228,7 +227,7 @@ impl DefaultFolderBuilder {
 
 #[cfg(feature = "flowy_unit_test")]
 impl FolderManager {
-    pub async fn folder_editor(&self) -> Arc<ClientFolderEditor> {
+    pub async fn folder_editor(&self) -> Arc<FolderEditor> {
         self.folder_editor.read().await.clone().unwrap()
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs

@@ -46,7 +46,7 @@ pub(crate) async fn update_app_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, app_controller, view_controller))]
+#[tracing::instrument(level = "trace", skip(data, app_controller, view_controller))]
 pub(crate) async fn read_app_handler(
     data: Data<AppId>,
     app_controller: AppData<Arc<AppController>>,

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

@@ -17,7 +17,7 @@ use lib_ot::core::PlainTextAttributes;
 use parking_lot::RwLock;
 use std::sync::Arc;
 
-pub struct ClientFolderEditor {
+pub struct FolderEditor {
     user_id: String,
     #[allow(dead_code)]
     pub(crate) folder_id: FolderId,
@@ -27,7 +27,7 @@ pub struct ClientFolderEditor {
     ws_manager: Arc<flowy_revision::RevisionWebSocketManager>,
 }
 
-impl ClientFolderEditor {
+impl FolderEditor {
     #[allow(unused_variables)]
     pub async fn new(
         user_id: &str,
@@ -129,7 +129,7 @@ impl RevisionCloudService for FolderRevisionCloudService {
 }
 
 #[cfg(feature = "flowy_unit_test")]
-impl ClientFolderEditor {
+impl FolderEditor {
     pub fn rev_manager(&self) -> Arc<RevisionManager> {
         self.rev_manager.clone()
     }

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

@@ -5,7 +5,7 @@ mod version_2;
 use crate::{
     event_map::WorkspaceDatabase,
     manager::FolderId,
-    services::{folder_editor::ClientFolderEditor, persistence::migration::FolderMigration},
+    services::{folder_editor::FolderEditor, persistence::migration::FolderMigration},
 };
 use flowy_database::ConnectionPool;
 use flowy_error::{FlowyError, FlowyResult};
@@ -50,14 +50,11 @@ pub trait FolderPersistenceTransaction {
 
 pub struct FolderPersistence {
     database: Arc<dyn WorkspaceDatabase>,
-    folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
+    folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>,
 }
 
 impl FolderPersistence {
-    pub fn new(
-        database: Arc<dyn WorkspaceDatabase>,
-        folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
-    ) -> Self {
+    pub fn new(database: Arc<dyn WorkspaceDatabase>, folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>) -> Self {
         Self {
             database,
             folder_editor,

+ 2 - 2
frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs

@@ -1,5 +1,5 @@
 use crate::services::{
-    folder_editor::ClientFolderEditor,
+    folder_editor::FolderEditor,
     persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset},
 };
 use flowy_error::{FlowyError, FlowyResult};
@@ -11,7 +11,7 @@ use flowy_folder_data_model::entities::{
 };
 use std::sync::Arc;
 
-impl FolderPersistenceTransaction for ClientFolderEditor {
+impl FolderPersistenceTransaction for FolderEditor {
     fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
         if let Some(change) = self.folder.write().create_workspace(workspace)? {
             let _ = self.apply_change(change)?;

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

@@ -129,7 +129,7 @@ impl ViewController {
             .await
     }
 
-    #[tracing::instrument(level = "debug", skip(self), err)]
+    #[tracing::instrument(level = "trace", skip(self), err)]
     pub(crate) fn set_latest_view(&self, view_id: &str) -> Result<(), FlowyError> {
         KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
         Ok(())
@@ -193,7 +193,7 @@ impl ViewController {
     }
 
     // belong_to_id will be the app_id or view_id.
-    #[tracing::instrument(level = "debug", skip(self), err)]
+    #[tracing::instrument(level = "trace", skip(self), err)]
     pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, FlowyError> {
         self.persistence
             .begin_transaction(|transaction| {

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

@@ -1,5 +1,5 @@
 use flowy_folder::event_map::FolderEvent::*;
-use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor};
+use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor};
 use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId};
 use flowy_folder_data_model::entities::workspace::WorkspaceId;
 use flowy_folder_data_model::entities::{
@@ -125,7 +125,7 @@ impl FolderTest {
 
     pub async fn run_script(&mut self, script: FolderScript) {
         let sdk = &self.sdk;
-        let folder_editor: Arc<ClientFolderEditor> = sdk.folder_manager.folder_editor().await;
+        let folder_editor: Arc<FolderEditor> = sdk.folder_manager.folder_editor().await;
         let rev_manager = folder_editor.rev_manager();
         let cache = rev_manager.revision_cache().await;
 

+ 17 - 38
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -7,7 +7,7 @@ use flowy_grid_data_model::entities::*;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_grid_data_handler(
     data: Data<GridId>,
     manager: AppData<Arc<GridManager>>,
@@ -34,7 +34,7 @@ pub(crate) async fn get_grid_blocks_handler(
     data_result(repeated_grid_block)
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_fields_handler(
     data: Data<QueryFieldPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -47,7 +47,7 @@ pub(crate) async fn get_fields_handler(
     data_result(repeated_field)
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn update_field_handler(
     data: Data<FieldChangesetPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -58,7 +58,7 @@ pub(crate) async fn update_field_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn insert_field_handler(
     data: Data<InsertFieldPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -69,7 +69,7 @@ pub(crate) async fn insert_field_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn update_field_type_option_handler(
     data: Data<UpdateFieldTypeOptionPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -82,7 +82,7 @@ pub(crate) async fn update_field_type_option_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn delete_field_handler(
     data: Data<FieldIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -93,7 +93,7 @@ pub(crate) async fn delete_field_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn switch_to_field_handler(
     data: Data<EditFieldPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -120,7 +120,7 @@ pub(crate) async fn switch_to_field_handler(
     data_result(data)
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn duplicate_field_handler(
     data: Data<FieldIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -132,7 +132,7 @@ pub(crate) async fn duplicate_field_handler(
 }
 
 /// Return the FieldTypeOptionData if the Field exists otherwise return record not found error.
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_field_type_option_data_handler(
     data: Data<EditFieldPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -154,7 +154,7 @@ pub(crate) async fn get_field_type_option_data_handler(
 }
 
 /// Create FieldMeta and save it. Return the FieldTypeOptionData.
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn create_field_type_option_data_handler(
     data: Data<EditFieldPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -171,7 +171,7 @@ pub(crate) async fn create_field_type_option_data_handler(
     })
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn move_item_handler(
     data: Data<MoveItemPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -252,7 +252,7 @@ pub(crate) async fn get_cell_handler(
     }
 }
 
-#[tracing::instrument(level = "debug", skip_all, err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_cell_handler(
     data: Data<CellChangeset>,
     manager: AppData<Arc<GridManager>>,
@@ -263,28 +263,7 @@ pub(crate) async fn update_cell_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "trace", skip(data, manager), err)]
-pub(crate) async fn get_date_cell_data_handler(
-    data: Data<CellIdentifierPayload>,
-    manager: AppData<Arc<GridManager>>,
-) -> DataResult<DateCellData, FlowyError> {
-    let params: CellIdentifier = data.into_inner().try_into()?;
-    let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor.get_field_meta(&params.field_id).await {
-        None => {
-            tracing::error!("Can't find the date field with id: {}", params.field_id);
-            data_result(DateCellData::default())
-        }
-        Some(field_meta) => {
-            let cell_meta = editor.get_cell_meta(&params.row_id, &params.field_id).await?;
-            let type_option = DateTypeOption::from(&field_meta);
-            let date_cell_data = type_option.make_date_cell_data(&cell_meta)?;
-            data_result(date_cell_data)
-        }
-    }
-}
-
-#[tracing::instrument(level = "debug", skip_all, err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn new_select_option_handler(
     data: Data<CreateSelectOptionPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -301,7 +280,7 @@ pub(crate) async fn new_select_option_handler(
     }
 }
 
-#[tracing::instrument(level = "debug", skip_all, err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_select_option_handler(
     data: Data<SelectOptionChangesetPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -341,7 +320,7 @@ pub(crate) async fn update_select_option_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_select_option_handler(
     data: Data<CellIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -362,7 +341,7 @@ pub(crate) async fn get_select_option_handler(
     }
 }
 
-#[tracing::instrument(level = "debug", skip_all, err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_select_option_cell_handler(
     data: Data<SelectOptionCellChangesetPayload>,
     manager: AppData<Arc<GridManager>>,
@@ -373,7 +352,7 @@ pub(crate) async fn update_select_option_cell_handler(
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip_all, err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_date_cell_handler(
     data: Data<DateChangesetPayload>,
     manager: AppData<Arc<GridManager>>,

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

@@ -29,7 +29,6 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         // Cell
         .event(GridEvent::GetCell, get_cell_handler)
         .event(GridEvent::UpdateCell, update_cell_handler)
-        .event(GridEvent::GetDateCellData, get_date_cell_data_handler)
         // SelectOption
         .event(GridEvent::NewSelectOption, new_select_option_handler)
         .event(GridEvent::UpdateSelectOption, update_select_option_handler)
@@ -112,7 +111,4 @@ pub enum GridEvent {
 
     #[event(input = "DateChangesetPayload")]
     UpdateDateCell = 80,
-
-    #[event(input = "CellIdentifierPayload", output = "DateCellData")]
-    GetDateCellData = 90,
 }

+ 20 - 44
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -1,5 +1,5 @@
-use crate::services::grid_editor::ClientGridEditor;
-use crate::services::persistence::block_index::BlockIndexPersistence;
+use crate::services::grid_editor::GridMetaEditor;
+use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::persistence::kv::GridKVPersistence;
 use crate::services::persistence::GridDatabase;
 use bytes::Bytes;
@@ -20,9 +20,9 @@ pub trait GridUser: Send + Sync {
 }
 
 pub struct GridManager {
-    editor_map: Arc<GridEditorMap>,
+    editor_map: Arc<DashMap<String, Arc<GridMetaEditor>>>,
     grid_user: Arc<dyn GridUser>,
-    block_index_persistence: Arc<BlockIndexPersistence>,
+    block_index_cache: Arc<BlockIndexCache>,
     #[allow(dead_code)]
     kv_persistence: Arc<GridKVPersistence>,
 }
@@ -33,14 +33,14 @@ impl GridManager {
         _rev_web_socket: Arc<dyn RevisionWebSocket>,
         database: Arc<dyn GridDatabase>,
     ) -> Self {
-        let grid_editors = Arc::new(GridEditorMap::new());
+        let grid_editors = Arc::new(DashMap::new());
         let kv_persistence = Arc::new(GridKVPersistence::new(database.clone()));
-        let block_index_persistence = Arc::new(BlockIndexPersistence::new(database));
+        let block_index_persistence = Arc::new(BlockIndexCache::new(database));
         Self {
             editor_map: grid_editors,
             grid_user,
             kv_persistence,
-            block_index_persistence,
+            block_index_cache: block_index_persistence,
         }
     }
 
@@ -67,7 +67,7 @@ impl GridManager {
     }
 
     #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
-    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<ClientGridEditor>> {
+    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<GridMetaEditor>> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
         self.get_or_create_grid_editor(grid_id).await
@@ -90,23 +90,27 @@ impl GridManager {
     }
 
     // #[tracing::instrument(level = "debug", skip(self), err)]
-    pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
+    pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
         match self.editor_map.get(grid_id) {
             None => Err(FlowyError::internal().context("Should call open_grid function first")),
-            Some(editor) => Ok(editor),
+            Some(editor) => Ok(editor.clone()),
         }
     }
 
-    async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
+    async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
         match self.editor_map.get(grid_id) {
             None => {
                 tracing::trace!("Create grid editor with id: {}", grid_id);
                 let db_pool = self.grid_user.db_pool()?;
                 let editor = self.make_grid_editor(grid_id, db_pool).await?;
-                self.editor_map.insert(grid_id, &editor);
+
+                if self.editor_map.contains_key(grid_id) {
+                    tracing::warn!("Grid:{} already exists in cache", grid_id);
+                }
+                self.editor_map.insert(grid_id.to_string(), editor.clone());
                 Ok(editor)
             }
-            Some(editor) => Ok(editor),
+            Some(editor) => Ok(editor.clone()),
         }
     }
 
@@ -115,11 +119,10 @@ impl GridManager {
         &self,
         grid_id: &str,
         pool: Arc<ConnectionPool>,
-    ) -> Result<Arc<ClientGridEditor>, FlowyError> {
+    ) -> Result<Arc<GridMetaEditor>, FlowyError> {
         let user = self.grid_user.clone();
         let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
-        let grid_editor =
-            ClientGridEditor::new(grid_id, user, rev_manager, self.block_index_persistence.clone()).await?;
+        let grid_editor = GridMetaEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
         Ok(grid_editor)
     }
 
@@ -145,31 +148,6 @@ impl GridManager {
     }
 }
 
-pub struct GridEditorMap {
-    inner: DashMap<String, Arc<ClientGridEditor>>,
-}
-
-impl GridEditorMap {
-    fn new() -> Self {
-        Self { inner: DashMap::new() }
-    }
-
-    pub(crate) fn insert(&self, grid_id: &str, grid_editor: &Arc<ClientGridEditor>) {
-        if self.inner.contains_key(grid_id) {
-            tracing::warn!("Grid:{} already exists in cache", grid_id);
-        }
-        self.inner.insert(grid_id.to_string(), grid_editor.clone());
-    }
-
-    pub(crate) fn get(&self, grid_id: &str) -> Option<Arc<ClientGridEditor>> {
-        Some(self.inner.get(grid_id)?.clone())
-    }
-
-    pub(crate) fn remove(&self, grid_id: &str) {
-        self.inner.remove(grid_id);
-    }
-}
-
 pub async fn make_grid_view_data(
     user_id: &str,
     view_id: &str,
@@ -192,9 +170,7 @@ pub async fn make_grid_view_data(
 
     // Indexing the block's rows
     build_context.block_meta_data.rows.iter().for_each(|row| {
-        let _ = grid_manager
-            .block_index_persistence
-            .insert_or_update(&row.block_id, &row.id);
+        let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
     });
 
     // Create grid's block

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

@@ -48,7 +48,6 @@ pub enum GridEvent {
     UpdateCell = 71,
     UpdateSelectOptionCell = 72,
     UpdateDateCell = 80,
-    GetDateCellData = 90,
 }
 
 impl ::protobuf::ProtobufEnum for GridEvent {
@@ -81,7 +80,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             71 => ::std::option::Option::Some(GridEvent::UpdateCell),
             72 => ::std::option::Option::Some(GridEvent::UpdateSelectOptionCell),
             80 => ::std::option::Option::Some(GridEvent::UpdateDateCell),
-            90 => ::std::option::Option::Some(GridEvent::GetDateCellData),
             _ => ::std::option::Option::None
         }
     }
@@ -111,7 +109,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::UpdateCell,
             GridEvent::UpdateSelectOptionCell,
             GridEvent::UpdateDateCell,
-            GridEvent::GetDateCellData,
         ];
         values
     }
@@ -140,7 +137,7 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xdc\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
+    \n\x0fevent_map.proto*\xc7\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
     \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
     \x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\
     \x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\
@@ -151,7 +148,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteR\
     ow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\
     \n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\
-    \x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\
+    \x0eUpdateDateCell\x10Pb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -24,5 +24,4 @@ enum GridEvent {
     UpdateCell = 71;
     UpdateSelectOptionCell = 72;
     UpdateDateCell = 80;
-    GetDateCellData = 90;
 }

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

@@ -8,18 +8,17 @@ use flowy_sync::util::make_delta_from_revisions;
 use lib_infra::future::FutureResult;
 use lib_ot::core::PlainTextAttributes;
 use std::borrow::Cow;
-
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
-pub struct ClientGridBlockMetaEditor {
+pub struct GridBlockMetaEditor {
     user_id: String,
     pub block_id: String,
     pad: Arc<RwLock<GridBlockMetaPad>>,
     rev_manager: Arc<RevisionManager>,
 }
 
-impl ClientGridBlockMetaEditor {
+impl GridBlockMetaEditor {
     pub async fn new(
         user_id: &str,
         token: &str,

+ 18 - 18
frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs

@@ -1,7 +1,7 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::manager::GridUser;
-use crate::services::block_meta_editor::ClientGridBlockMetaEditor;
-use crate::services::persistence::block_index::BlockIndexPersistence;
+use crate::services::block_meta_editor::GridBlockMetaEditor;
+use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::row::{group_row_orders, GridBlockSnapshot};
 use dashmap::DashMap;
 use flowy_error::FlowyResult;
@@ -15,20 +15,20 @@ use std::borrow::Cow;
 use std::collections::HashMap;
 use std::sync::Arc;
 
-pub(crate) struct GridBlockMetaEditorManager {
+type BlockId = String;
+pub(crate) struct GridBlockManager {
     grid_id: String,
     user: Arc<dyn GridUser>,
-    // Key: block_id
-    editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
-    persistence: Arc<BlockIndexPersistence>,
+    persistence: Arc<BlockIndexCache>,
+    block_editor_map: DashMap<BlockId, Arc<GridBlockMetaEditor>>,
 }
 
-impl GridBlockMetaEditorManager {
+impl GridBlockManager {
     pub(crate) async fn new(
         grid_id: &str,
         user: &Arc<dyn GridUser>,
         blocks: Vec<GridBlockMeta>,
-        persistence: Arc<BlockIndexPersistence>,
+        persistence: Arc<BlockIndexCache>,
     ) -> FlowyResult<Self> {
         let editor_map = make_block_meta_editor_map(user, blocks).await?;
         let user = user.clone();
@@ -36,27 +36,27 @@ impl GridBlockMetaEditorManager {
         let manager = Self {
             grid_id,
             user,
-            editor_map,
+            block_editor_map: editor_map,
             persistence,
         };
         Ok(manager)
     }
 
     // #[tracing::instrument(level = "trace", skip(self))]
-    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
+    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
         debug_assert!(!block_id.is_empty());
-        match self.editor_map.get(block_id) {
+        match self.block_editor_map.get(block_id) {
             None => {
                 tracing::error!("The is a fatal error, block is not exist");
                 let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
-                self.editor_map.insert(block_id.to_owned(), editor.clone());
+                self.block_editor_map.insert(block_id.to_owned(), editor.clone());
                 Ok(editor)
             }
             Some(editor) => Ok(editor.clone()),
         }
     }
 
-    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
+    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
         let block_id = self.persistence.get_block_id(row_id)?;
         Ok(self.get_editor(&block_id).await?)
     }
@@ -67,7 +67,7 @@ impl GridBlockMetaEditorManager {
         row_meta: RowMeta,
         start_row_id: Option<String>,
     ) -> FlowyResult<i32> {
-        let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
+        let _ = self.persistence.insert(&row_meta.block_id, &row_meta.id)?;
         let editor = self.get_editor(&row_meta.block_id).await?;
 
         let mut index_row_order = IndexRowOrder::from(&row_meta);
@@ -90,7 +90,7 @@ impl GridBlockMetaEditorManager {
             let editor = self.get_editor(&block_id).await?;
             let mut row_count = 0;
             for row in row_metas {
-                let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
+                let _ = self.persistence.insert(&row.block_id, &row.id)?;
                 let mut row_order = IndexRowOrder::from(&row);
                 let (count, index) = editor.create_row(row, None).await?;
                 row_count = count;
@@ -256,7 +256,7 @@ impl GridBlockMetaEditorManager {
 async fn make_block_meta_editor_map(
     user: &Arc<dyn GridUser>,
     blocks: Vec<GridBlockMeta>,
-) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
+) -> FlowyResult<DashMap<String, Arc<GridBlockMetaEditor>>> {
     let editor_map = DashMap::new();
     for block in blocks {
         let editor = make_block_meta_editor(user, &block.block_id).await?;
@@ -266,7 +266,7 @@ async fn make_block_meta_editor_map(
     Ok(editor_map)
 }
 
-async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
+async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockMetaEditor> {
     let token = user.token()?;
     let user_id = user.user_id()?;
     let pool = user.db_pool()?;
@@ -274,5 +274,5 @@ async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> Flo
     let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
     let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
     let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
-    ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
+    GridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
 }

+ 7 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs

@@ -58,7 +58,7 @@ impl CellDataOperation<String, String> for CheckboxTypeOption {
 
         let encoded_data = encoded_data.into();
         if encoded_data == YES || encoded_data == NO {
-            return Ok(DecodedCellData::from_content(encoded_data));
+            return Ok(DecodedCellData::new(encoded_data));
         }
 
         Ok(DecodedCellData::default())
@@ -104,37 +104,37 @@ mod tests {
         let field_meta = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
         let data = apply_cell_data_changeset("true", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             YES
         );
 
         let data = apply_cell_data_changeset("1", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             YES
         );
 
         let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             YES
         );
 
         let data = apply_cell_data_changeset("false", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             NO
         );
 
         let data = apply_cell_data_changeset("no", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             NO
         );
 
         let data = apply_cell_data_changeset("12", None, &field_meta).unwrap();
         assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
+            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
             NO
         );
     }

+ 64 - 119
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -1,9 +1,7 @@
 use crate::entities::{CellIdentifier, CellIdentifierPayload};
 use crate::impl_type_option;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
-use crate::services::row::{
-    CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData, TypeOptionCellData,
-};
+use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
 use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
 use chrono::NaiveDateTime;
@@ -79,32 +77,12 @@ impl DateTypeOption {
         }
     }
 
-    pub fn make_date_cell_data(&self, cell_meta: &Option<CellMeta>) -> FlowyResult<DateCellData> {
-        if cell_meta.is_none() {
-            return Ok(DateCellData::default());
-        }
-
-        let json = &cell_meta.as_ref().unwrap().data;
-        let result = TypeOptionCellData::from_str(json);
-        if result.is_err() {
-            return Ok(DateCellData::default());
-        }
-
-        let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
-        let date = self.decode_cell_data_from_timestamp(&serde_cell_data).content;
-        let time = serde_cell_data.time.unwrap_or_else(|| "".to_owned());
-        let timestamp = serde_cell_data.timestamp;
-
-        Ok(DateCellData { date, time, timestamp })
-    }
-
-    fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData {
+    fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String {
         if serde_cell_data.timestamp == 0 {
-            return DecodedCellData::default();
+            return "".to_owned();
         }
 
-        let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time);
-        DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content)
+        self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time)
     }
 
     fn timestamp_from_utc_with_time(
@@ -156,7 +134,11 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
         }
 
         let encoded_data = encoded_data.into().try_into_inner()?;
-        Ok(self.decode_cell_data_from_timestamp(&encoded_data))
+        let date = self.date_desc_from_timestamp(&encoded_data);
+        let time = encoded_data.time.unwrap_or_else(|| "".to_owned());
+        let timestamp = encoded_data.timestamp;
+
+        DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp })
     }
 
     fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<DateCellDataSerde, FlowyError>
@@ -417,20 +399,27 @@ impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
 #[cfg(test)]
 mod tests {
     use crate::services::field::FieldBuilder;
-    use crate::services::field::{DateCellContentChangeset, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat};
-    use crate::services::row::{
-        apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data, CellDataOperation, EncodedCellData,
+    use crate::services::field::{
+        DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
     };
+    use crate::services::row::{CellDataOperation, EncodedCellData};
     use flowy_grid_data_model::entities::{FieldMeta, FieldType};
     use strum::IntoEnumIterator;
 
     #[test]
     fn date_description_invalid_input_test() {
-        let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
-        let data = apply_cell_data_changeset("1e", None, &field_meta).unwrap();
-        assert_eq!(
-            decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
-            "".to_owned()
+        let type_option = DateTypeOption::default();
+        let field_type = FieldType::DateTime;
+        let field_meta = FieldBuilder::from_field_type(&field_type).build();
+        assert_changeset_result(
+            &type_option,
+            DateCellContentChangeset {
+                date: Some("1e".to_string()),
+                time: Some("23:00".to_owned()),
+            },
+            &field_type,
+            &field_meta,
+            "",
         );
     }
 
@@ -442,40 +431,16 @@ mod tests {
             type_option.date_format = date_format;
             match date_format {
                 DateFormat::Friendly => {
-                    assert_eq!(
-                        "Mar 14,2022".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
+                    assert_decode_timestamp(1647251762, &type_option, &field_meta, "Mar 14,2022");
                 }
                 DateFormat::US => {
-                    assert_eq!(
-                        "2022/03/14".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
+                    assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
                 }
                 DateFormat::ISO => {
-                    assert_eq!(
-                        "2022-03-14".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
+                    assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022-03-14");
                 }
                 DateFormat::Local => {
-                    assert_eq!(
-                        "2022/03/14".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
+                    assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
                 }
             }
         }
@@ -483,43 +448,6 @@ mod tests {
 
     #[test]
     fn date_description_time_format_test() {
-        let mut type_option = DateTypeOption::default();
-        let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
-        for time_format in TimeFormat::iter() {
-            type_option.time_format = time_format;
-            match time_format {
-                TimeFormat::TwentyFourHour => {
-                    assert_eq!(
-                        "Mar 14,2022".to_owned(),
-                        type_option.today_desc_from_timestamp(1647251762, &None)
-                    );
-                    assert_eq!(
-                        "Mar 14,2022".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
-                }
-                TimeFormat::TwelveHour => {
-                    assert_eq!(
-                        "Mar 14,2022".to_owned(),
-                        type_option.today_desc_from_timestamp(1647251762, &None)
-                    );
-                    assert_eq!(
-                        "Mar 14,2022".to_owned(),
-                        type_option
-                            .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
-                            .unwrap()
-                            .content
-                    );
-                }
-            }
-        }
-    }
-
-    #[test]
-    fn date_description_time_format_test2() {
         let mut type_option = DateTypeOption::default();
         let field_type = FieldType::DateTime;
         let field_meta = FieldBuilder::from_field_type(&field_type).build();
@@ -529,7 +457,7 @@ mod tests {
             type_option.include_time = true;
             match time_format {
                 TimeFormat::TwentyFourHour => {
-                    assert_result(
+                    assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
                             date: Some(1653609600.to_string()),
@@ -539,7 +467,7 @@ mod tests {
                         &field_meta,
                         "May 27,2022 00:00",
                     );
-                    assert_result(
+                    assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
                             date: Some(1653609600.to_string()),
@@ -551,7 +479,7 @@ mod tests {
                     );
                 }
                 TimeFormat::TwelveHour => {
-                    assert_result(
+                    assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
                             date: Some(1653609600.to_string()),
@@ -562,7 +490,7 @@ mod tests {
                         "May 27,2022 12:00 AM",
                     );
 
-                    assert_result(
+                    assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
                             date: Some(1653609600.to_string()),
@@ -573,7 +501,7 @@ mod tests {
                         "May 27,2022",
                     );
 
-                    assert_result(
+                    assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
                             date: Some(1653609600.to_string()),
@@ -595,7 +523,7 @@ mod tests {
         let field_meta = FieldBuilder::from_field_type(&field_type).build();
         let date_timestamp = "1653609600".to_owned();
 
-        assert_result(
+        assert_changeset_result(
             &type_option,
             DateCellContentChangeset {
                 date: Some(date_timestamp.clone()),
@@ -607,7 +535,7 @@ mod tests {
         );
 
         type_option.include_time = true;
-        assert_result(
+        assert_changeset_result(
             &type_option,
             DateCellContentChangeset {
                 date: Some(date_timestamp.clone()),
@@ -618,7 +546,7 @@ mod tests {
             "May 27,2022 00:00",
         );
 
-        assert_result(
+        assert_changeset_result(
             &type_option,
             DateCellContentChangeset {
                 date: Some(date_timestamp.clone()),
@@ -630,7 +558,7 @@ mod tests {
         );
 
         type_option.time_format = TimeFormat::TwelveHour;
-        assert_result(
+        assert_changeset_result(
             &type_option,
             DateCellContentChangeset {
                 date: Some(date_timestamp),
@@ -670,22 +598,39 @@ mod tests {
         type_option.apply_changeset("he", None).unwrap();
     }
 
-    fn data(s: i64) -> String {
-        serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap()
-    }
-
-    fn assert_result(
+    fn assert_changeset_result(
         type_option: &DateTypeOption,
         changeset: DateCellContentChangeset,
-        field_type: &FieldType,
+        _field_type: &FieldType,
         field_meta: &FieldMeta,
         expected: &str,
     ) {
         let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap()));
-        let content = type_option
-            .decode_cell_data(encoded_data, field_type, field_meta)
+        assert_eq!(
+            expected.to_owned(),
+            decode_cell_data(encoded_data, type_option, field_meta)
+        );
+    }
+
+    fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) {
+        let serde_json = DateCellDataSerde { timestamp, time: None }.to_string();
+
+        assert_eq!(
+            expected.to_owned(),
+            decode_cell_data(serde_json, type_option, field_meta)
+        );
+    }
+
+    fn decode_cell_data<T: Into<EncodedCellData<DateCellDataSerde>>>(
+        encoded_data: T,
+        type_option: &DateTypeOption,
+        field_meta: &FieldMeta,
+    ) -> String {
+        type_option
+            .decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta)
+            .unwrap()
+            .parse::<DateCellData>()
             .unwrap()
-            .content;
-        assert_eq!(expected.to_owned(), content);
+            .date
     }
 }

+ 5 - 5
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs

@@ -94,22 +94,22 @@ impl CellDataOperation<String, String> for NumberTypeOption {
         match self.format {
             NumberFormat::Number => {
                 if let Ok(v) = cell_data.parse::<f64>() {
-                    return Ok(DecodedCellData::from_content(v.to_string()));
+                    return Ok(DecodedCellData::new(v.to_string()));
                 }
 
                 if let Ok(v) = cell_data.parse::<i64>() {
-                    return Ok(DecodedCellData::from_content(v.to_string()));
+                    return Ok(DecodedCellData::new(v.to_string()));
                 }
 
                 Ok(DecodedCellData::default())
             }
             NumberFormat::Percent => {
                 let content = cell_data.parse::<f64>().map_or(String::new(), |v| v.to_string());
-                Ok(DecodedCellData::from_content(content))
+                Ok(DecodedCellData::new(content))
             }
             _ => {
                 let content = self.money_from_str(&cell_data);
-                Ok(DecodedCellData::from_content(content))
+                Ok(DecodedCellData::new(content))
             }
         }
     }
@@ -738,7 +738,7 @@ mod tests {
             type_option
                 .decode_cell_data(cell_data, field_type, field_meta)
                 .unwrap()
-                .content,
+                .to_string(),
             expected_str.to_owned()
         );
     }

+ 73 - 74
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

@@ -109,16 +109,18 @@ impl CellDataOperation<String, String> for SingleSelectTypeOption {
             return Ok(DecodedCellData::default());
         }
 
-        let cell_data = encoded_data.into();
-        if let Some(option_id) = select_option_ids(cell_data).first() {
-            let data = match self.options.iter().find(|option| &option.id == option_id) {
-                None => DecodedCellData::default(),
-                Some(option) => DecodedCellData::from_content(option.name.clone()),
-            };
-            Ok(data)
-        } else {
-            Ok(DecodedCellData::default())
+        let encoded_data = encoded_data.into();
+        let mut cell_data = SelectOptionCellData {
+            options: self.options.clone(),
+            select_options: vec![],
+        };
+        if let Some(option_id) = select_option_ids(encoded_data).first() {
+            if let Some(option) = self.options.iter().find(|option| &option.id == option_id) {
+                cell_data.select_options.push(option.clone());
+            }
         }
+
+        DecodedCellData::try_from_bytes(cell_data)
     }
 
     fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
@@ -204,17 +206,21 @@ impl CellDataOperation<String, String> for MultiSelectTypeOption {
         if !decoded_field_type.is_select_option() {
             return Ok(DecodedCellData::default());
         }
-        let cell_data = encoded_data.into();
-        let option_ids = select_option_ids(cell_data);
-        let content = self
-            .options
-            .iter()
-            .filter(|option| option_ids.contains(&option.id))
-            .map(|option| option.name.clone())
-            .collect::<Vec<String>>()
-            .join(SELECTION_IDS_SEPARATOR);
 
-        Ok(DecodedCellData::from_content(content))
+        tracing::info!("😁{}", self.options.len());
+
+        let encoded_data = encoded_data.into();
+        let select_options = select_option_ids(encoded_data)
+            .into_iter()
+            .flat_map(|option_id| self.options.iter().find(|option| option.id == option_id).cloned())
+            .collect::<Vec<SelectOption>>();
+
+        let cell_data = SelectOptionCellData {
+            options: self.options.clone(),
+            select_options,
+        };
+
+        DecodedCellData::try_from_bytes(cell_data)
     }
 
     fn apply_changeset<T>(&self, changeset: T, cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
@@ -280,7 +286,7 @@ fn select_option_ids(data: String) -> Vec<String> {
         .collect::<Vec<String>>()
 }
 
-#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
+#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
 pub struct SelectOption {
     #[pb(index = 1)]
     pub id: String,
@@ -446,7 +452,7 @@ pub struct SelectOptionCellData {
     pub select_options: Vec<SelectOption>,
 }
 
-#[derive(ProtoBuf_Enum, Serialize, Deserialize, Debug, Clone)]
+#[derive(ProtoBuf_Enum, PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
 #[repr(u8)]
 pub enum SelectOptionColor {
     Purple = 0,
@@ -502,9 +508,10 @@ mod tests {
     use crate::services::field::FieldBuilder;
     use crate::services::field::{
         MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset,
-        SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
+        SelectOptionCellData, SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
     };
     use crate::services::row::CellDataOperation;
+    use flowy_grid_data_model::entities::FieldMeta;
 
     #[test]
     fn single_select_test() {
@@ -526,47 +533,24 @@ mod tests {
         let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR);
         let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
         let cell_data = type_option.apply_changeset(data, None).unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            google_option.name,
-        );
+        assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
 
         let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
         let cell_data = type_option.apply_changeset(data, None).unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            google_option.name,
-        );
+        assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
 
         // Invalid option id
         let cell_data = type_option
             .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
             .unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            "",
-        );
+        assert_single_select_options(cell_data, &type_option, &field_meta, vec![]);
 
         // Invalid option id
         let cell_data = type_option
             .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None)
             .unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            "",
-        );
+
+        assert_single_select_options(cell_data, &type_option, &field_meta, vec![]);
 
         // Invalid changeset
         assert!(type_option.apply_changeset("123", None).is_err());
@@ -592,49 +576,64 @@ mod tests {
         let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
         let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
         let cell_data = type_option.apply_changeset(data, None).unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR),
+        assert_multi_select_options(
+            cell_data,
+            &type_option,
+            &field_meta,
+            vec![google_option.clone(), facebook_option.clone()],
         );
 
         let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
         let cell_data = type_option.apply_changeset(data, None).unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            google_option.name,
-        );
+        assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
 
         // Invalid option id
         let cell_data = type_option
             .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
             .unwrap();
-        assert_eq!(
-            type_option
-                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
-                .unwrap()
-                .content,
-            "",
-        );
+        assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]);
 
         // Invalid option id
         let cell_data = type_option
             .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None)
             .unwrap();
+        assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]);
+
+        // Invalid changeset
+        assert!(type_option.apply_changeset("123", None).is_err());
+    }
+
+    fn assert_multi_select_options(
+        cell_data: String,
+        type_option: &MultiSelectTypeOption,
+        field_meta: &FieldMeta,
+        expected: Vec<SelectOption>,
+    ) {
         assert_eq!(
+            expected,
             type_option
                 .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
                 .unwrap()
-                .content,
-            "",
+                .parse::<SelectOptionCellData>()
+                .unwrap()
+                .select_options,
         );
+    }
 
-        // Invalid changeset
-        assert!(type_option.apply_changeset("123", None).is_err());
+    fn assert_single_select_options(
+        cell_data: String,
+        type_option: &SingleSelectTypeOption,
+        field_meta: &FieldMeta,
+        expected: Vec<SelectOption>,
+    ) {
+        assert_eq!(
+            expected,
+            type_option
+                .decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
+                .unwrap()
+                .parse::<SelectOptionCellData>()
+                .unwrap()
+                .select_options,
+        );
     }
 }

+ 16 - 10
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -49,7 +49,7 @@ impl CellDataOperation<String, String> for RichTextTypeOption {
             decode_cell_data(encoded_data, decoded_field_type, decoded_field_type, field_meta)
         } else {
             let cell_data = encoded_data.into();
-            Ok(DecodedCellData::from_content(cell_data))
+            Ok(DecodedCellData::new(cell_data))
         }
     }
 
@@ -85,22 +85,26 @@ mod tests {
             type_option
                 .decode_cell_data(json, &field_type, &date_time_field_meta)
                 .unwrap()
-                .content,
+                .parse::<DateCellData>()
+                .unwrap()
+                .date,
             "Mar 14,2022".to_owned()
         );
 
         // Single select
         let done_option = SelectOption::new("Done");
         let done_option_id = done_option.id.clone();
-        let single_select = SingleSelectTypeOptionBuilder::default().option(done_option);
+        let single_select = SingleSelectTypeOptionBuilder::default().option(done_option.clone());
         let single_select_field_meta = FieldBuilder::new(single_select).build();
 
         assert_eq!(
             type_option
                 .decode_cell_data(done_option_id, &FieldType::SingleSelect, &single_select_field_meta)
                 .unwrap()
-                .content,
-            "Done".to_owned()
+                .parse::<SelectOptionCellData>()
+                .unwrap()
+                .select_options,
+            vec![done_option],
         );
 
         // Multiple select
@@ -109,8 +113,8 @@ mod tests {
         let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
         let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str();
         let multi_select = MultiSelectTypeOptionBuilder::default()
-            .option(google_option)
-            .option(facebook_option);
+            .option(google_option.clone())
+            .option(facebook_option.clone());
         let multi_select_field_meta = FieldBuilder::new(multi_select).build();
         let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta);
         let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap();
@@ -118,8 +122,10 @@ mod tests {
             type_option
                 .decode_cell_data(cell_data, &FieldType::MultiSelect, &multi_select_field_meta)
                 .unwrap()
-                .content,
-            "Google,Facebook".to_owned()
+                .parse::<SelectOptionCellData>()
+                .unwrap()
+                .select_options,
+            vec![google_option, facebook_option]
         );
 
         //Number
@@ -129,7 +135,7 @@ mod tests {
             type_option
                 .decode_cell_data("18443".to_owned(), &FieldType::Number, &number_field_meta)
                 .unwrap()
-                .content,
+                .to_string(),
             "$18,443".to_owned()
         );
     }

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

@@ -1,9 +1,9 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::CellIdentifier;
 use crate::manager::GridUser;
-use crate::services::block_meta_manager::GridBlockMetaEditorManager;
+use crate::services::block_meta_manager::GridBlockManager;
 use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
-use crate::services::persistence::block_index::BlockIndexPersistence;
+use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::row::*;
 use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
@@ -19,20 +19,26 @@ use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
-pub struct ClientGridEditor {
+pub struct GridMetaEditor {
     grid_id: String,
     user: Arc<dyn GridUser>,
     grid_pad: Arc<RwLock<GridMetaPad>>,
     rev_manager: Arc<RevisionManager>,
-    block_meta_manager: Arc<GridBlockMetaEditorManager>,
+    block_manager: Arc<GridBlockManager>,
 }
 
-impl ClientGridEditor {
+impl Drop for GridMetaEditor {
+    fn drop(&mut self) {
+        tracing::trace!("Drop GridMetaEditor");
+    }
+}
+
+impl GridMetaEditor {
     pub async fn new(
         grid_id: &str,
         user: Arc<dyn GridUser>,
         mut rev_manager: RevisionManager,
-        persistence: Arc<BlockIndexPersistence>,
+        persistence: Arc<BlockIndexCache>,
     ) -> FlowyResult<Arc<Self>> {
         let token = user.token()?;
         let cloud = Arc::new(GridRevisionCloudService { token });
@@ -41,13 +47,13 @@ impl ClientGridEditor {
         let grid_pad = Arc::new(RwLock::new(grid_pad));
         let blocks = grid_pad.read().await.get_block_metas();
 
-        let block_meta_manager = Arc::new(GridBlockMetaEditorManager::new(grid_id, &user, blocks, persistence).await?);
+        let block_meta_manager = Arc::new(GridBlockManager::new(grid_id, &user, blocks, persistence).await?);
         Ok(Arc::new(Self {
             grid_id: grid_id.to_owned(),
             user,
             grid_pad,
             rev_manager,
-            block_meta_manager,
+            block_manager: block_meta_manager,
         }))
     }
 
@@ -254,10 +260,7 @@ impl ClientGridEditor {
         let row_order = RowOrder::from(&row_meta);
 
         // insert the row
-        let row_count = self
-            .block_meta_manager
-            .create_row(&block_id, row_meta, start_row_id)
-            .await?;
+        let row_count = self.block_manager.create_row(&block_id, row_meta, start_row_id).await?;
 
         // update block row count
         let changeset = GridBlockMetaChangeset::from_row_count(&block_id, row_count);
@@ -277,7 +280,7 @@ impl ClientGridEditor {
                 .or_insert_with(Vec::new)
                 .push(row_meta);
         }
-        let changesets = self.block_meta_manager.insert_row(rows_by_block_id).await?;
+        let changesets = self.block_manager.insert_row(rows_by_block_id).await?;
         for changeset in changesets {
             let _ = self.update_block(changeset).await?;
         }
@@ -286,7 +289,7 @@ impl ClientGridEditor {
 
     pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
         let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
-        self.block_meta_manager
+        self.block_manager
             .update_row(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta))
             .await
     }
@@ -309,7 +312,7 @@ impl ClientGridEditor {
     }
 
     pub async fn get_row(&self, row_id: &str) -> FlowyResult<Option<Row>> {
-        match self.block_meta_manager.get_row_meta(row_id).await? {
+        match self.block_manager.get_row_meta(row_id).await? {
             None => Ok(None),
             Some(row_meta) => {
                 let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
@@ -321,7 +324,7 @@ impl ClientGridEditor {
         }
     }
     pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
-        let _ = self.block_meta_manager.delete_row(row_id).await?;
+        let _ = self.block_manager.delete_row(row_id).await?;
         Ok(())
     }
 
@@ -331,12 +334,12 @@ impl ClientGridEditor {
 
     pub async fn get_cell(&self, params: &CellIdentifier) -> Option<Cell> {
         let field_meta = self.get_field_meta(&params.field_id).await?;
-        let row_meta = self.block_meta_manager.get_row_meta(&params.row_id).await.ok()??;
+        let row_meta = self.block_manager.get_row_meta(&params.row_id).await.ok()??;
         make_cell(&params.field_id, &field_meta, &row_meta)
     }
 
     pub async fn get_cell_meta(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellMeta>> {
-        let row_meta = self.block_meta_manager.get_row_meta(row_id).await?;
+        let row_meta = self.block_manager.get_row_meta(row_id).await?;
         match row_meta {
             None => Ok(None),
             Some(row_meta) => {
@@ -382,7 +385,7 @@ impl ClientGridEditor {
                     cell_content_changeset,
                 };
                 let _ = self
-                    .block_meta_manager
+                    .block_manager
                     .update_cell(cell_changeset, |row_meta| {
                         make_row_from_row_meta(&field_metas, row_meta)
                     })
@@ -403,7 +406,7 @@ impl ClientGridEditor {
     }
 
     pub async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<()> {
-        let changesets = self.block_meta_manager.delete_rows(row_orders).await?;
+        let changesets = self.block_manager.delete_rows(row_orders).await?;
         for changeset in changesets {
             let _ = self.update_block(changeset).await?;
         }
@@ -415,7 +418,7 @@ impl ClientGridEditor {
         let field_orders = pad_read_guard.get_field_orders();
         let mut block_orders = vec![];
         for block_order in pad_read_guard.get_block_metas() {
-            let row_orders = self.block_meta_manager.get_row_orders(&block_order.block_id).await?;
+            let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?;
             let block_order = GridBlockOrder {
                 block_id: block_order.block_id,
                 row_orders,
@@ -442,7 +445,7 @@ impl ClientGridEditor {
                 .collect::<Vec<String>>(),
             Some(block_ids) => block_ids,
         };
-        let snapshots = self.block_meta_manager.make_block_snapshots(block_ids).await?;
+        let snapshots = self.block_manager.make_block_snapshots(block_ids).await?;
         Ok(snapshots)
     }
 
@@ -476,10 +479,7 @@ impl ClientGridEditor {
     }
 
     pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
-        let _ = self
-            .block_meta_manager
-            .move_row(row_id, from as usize, to as usize)
-            .await?;
+        let _ = self.block_manager.move_row(row_id, from as usize, to as usize).await?;
         Ok(())
     }
 
@@ -565,7 +565,7 @@ impl ClientGridEditor {
 }
 
 #[cfg(feature = "flowy_unit_test")]
-impl ClientGridEditor {
+impl GridMetaEditor {
     pub fn rev_manager(&self) -> Arc<RevisionManager> {
         self.rev_manager.clone()
     }

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs

@@ -7,11 +7,11 @@ use flowy_database::{
 use flowy_error::FlowyResult;
 use std::sync::Arc;
 
-pub struct BlockIndexPersistence {
+pub struct BlockIndexCache {
     database: Arc<dyn GridDatabase>,
 }
 
-impl BlockIndexPersistence {
+impl BlockIndexCache {
     pub fn new(database: Arc<dyn GridDatabase>) -> Self {
         Self { database }
     }
@@ -26,7 +26,7 @@ impl BlockIndexPersistence {
         Ok(block_id)
     }
 
-    pub fn insert_or_update(&self, block_id: &str, row_id: &str) -> FlowyResult<()> {
+    pub fn insert(&self, block_id: &str, row_id: &str) -> FlowyResult<()> {
         let conn = self.database.db_connection()?;
         let item = IndexItem {
             row_id: row_id.to_string(),

+ 38 - 18
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -1,5 +1,6 @@
 use crate::services::field::*;
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use bytes::Bytes;
+use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType};
 use serde::{Deserialize, Serialize};
 use std::fmt::Formatter;
@@ -146,8 +147,15 @@ pub fn decode_cell_data_from_type_option_cell_data<T: TryInto<TypeOptionCellData
 ) -> DecodedCellData {
     if let Ok(type_option_cell_data) = data.try_into() {
         let (encoded_data, s_field_type) = type_option_cell_data.split();
-        decode_cell_data(encoded_data, &s_field_type, field_type, field_meta).unwrap_or_default()
+        match decode_cell_data(encoded_data, &s_field_type, field_type, field_meta) {
+            Ok(cell_data) => cell_data,
+            Err(e) => {
+                tracing::error!("Decode cell data failed, {:?}", e);
+                DecodedCellData::default()
+            }
+        }
     } else {
+        tracing::error!("Decode type option data failed");
         DecodedCellData::default()
     }
 }
@@ -159,6 +167,7 @@ pub fn decode_cell_data<T: Into<String>>(
     field_meta: &FieldMeta,
 ) -> FlowyResult<DecodedCellData> {
     let encoded_data = encoded_data.into();
+    tracing::info!("😁{:?}", field_meta.type_options);
     let get_cell_data = || {
         let data = match t_field_type {
             FieldType::RichText => field_meta
@@ -187,13 +196,7 @@ pub fn decode_cell_data<T: Into<String>>(
     };
 
     match get_cell_data() {
-        Some(Ok(data)) => {
-            tracing::Span::current().record(
-                "content",
-                &format!("{:?}: {}", field_meta.field_type, data.content).as_str(),
-            );
-            Ok(data)
-        }
+        Some(Ok(data)) => Ok(data),
         Some(Err(err)) => {
             tracing::error!("{:?}", err);
             Ok(DecodedCellData::default())
@@ -230,23 +233,40 @@ where
 
 #[derive(Default)]
 pub struct DecodedCellData {
-    raw: String,
-    pub content: String,
+    pub data: Vec<u8>,
 }
 
 impl DecodedCellData {
-    pub fn from_content(content: String) -> Self {
+    pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
         Self {
-            raw: content.clone(),
-            content,
+            data: data.as_ref().to_vec(),
         }
     }
 
-    pub fn new(raw: String, content: String) -> Self {
-        Self { raw, content }
+    pub fn try_from_bytes<T: TryInto<Bytes>>(bytes: T) -> FlowyResult<Self>
+    where
+        <T as TryInto<Bytes>>::Error: std::fmt::Debug,
+    {
+        let bytes = bytes.try_into().map_err(internal_error)?;
+        Ok(Self { data: bytes.to_vec() })
+    }
+
+    pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
+    where
+        <T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
+    {
+        T::try_from(self.data.as_ref()).map_err(internal_error)
     }
+}
 
-    pub fn split(self) -> (String, String) {
-        (self.raw, self.content)
+impl ToString for DecodedCellData {
+    fn to_string(&self) -> String {
+        match String::from_utf8(self.data.clone()) {
+            Ok(s) => s,
+            Err(e) => {
+                tracing::error!("DecodedCellData to string failed: {:?}", e);
+                "".to_string()
+            }
+        }
     }
 }

+ 4 - 6
frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs

@@ -31,17 +31,15 @@ pub fn make_cell_by_field_id(
     cell_meta: CellMeta,
 ) -> Option<(String, Cell)> {
     let field_meta = field_map.get(&field_id)?;
-    let (raw, content) =
-        decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split();
-    let cell = Cell::new(&field_id, content, raw);
+    let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data;
+    let cell = Cell::new(&field_id, data);
     Some((field_id, cell))
 }
 
 pub fn make_cell(field_id: &str, field_meta: &FieldMeta, row_meta: &RowMeta) -> Option<Cell> {
     let cell_meta = row_meta.cells.get(field_id)?.clone();
-    let (raw, content) =
-        decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split();
-    Some(Cell::new(field_id, content, raw))
+    let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data;
+    Some(Cell::new(field_id, data))
 }
 
 pub(crate) fn make_row_orders_from_row_metas(row_metas: &[Arc<RowMeta>]) -> Vec<RowOrder> {

+ 4 - 3
frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs

@@ -2,7 +2,7 @@ use crate::grid::script::EditorScript::*;
 use crate::grid::script::*;
 use chrono::NaiveDateTime;
 use flowy_grid::services::field::{
-    DateCellContentChangeset, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset,
+    DateCellContentChangeset, DateCellData, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset,
     SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
 };
 use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder};
@@ -295,8 +295,9 @@ async fn grid_row_add_date_cell_test() {
     let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
     assert_eq!(
         decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
-            .split()
-            .1,
+            .parse::<DateCellData>()
+            .unwrap()
+            .date,
         "2022/03/16",
     );
     let scripts = vec![CreateRow { context }];

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

@@ -1,6 +1,6 @@
 use bytes::Bytes;
 use flowy_grid::services::field::*;
-use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
+use flowy_grid::services::grid_editor::{GridMetaEditor, GridPadBuilder};
 use flowy_grid::services::row::CreateRowMetaPayload;
 use flowy_grid_data_model::entities::{
     BuildGridContext, CellChangeset, Field, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta,
@@ -72,7 +72,7 @@ pub enum EditorScript {
 pub struct GridEditorTest {
     pub sdk: FlowySDKTest,
     pub grid_id: String,
-    pub editor: Arc<ClientGridEditor>,
+    pub editor: Arc<GridMetaEditor>,
     pub field_metas: Vec<FieldMeta>,
     pub grid_blocks: Vec<GridBlockMeta>,
     pub row_metas: Vec<Arc<RowMeta>>,
@@ -239,7 +239,7 @@ impl GridEditorTest {
     }
 }
 
-async fn get_row_metas(editor: &Arc<ClientGridEditor>) -> Vec<Arc<RowMeta>> {
+async fn get_row_metas(editor: &Arc<GridMetaEditor>) -> Vec<Arc<RowMeta>> {
     editor
         .grid_block_snapshots(None)
         .await

+ 4 - 4
frontend/rust-lib/flowy-text-block/src/editor.rs

@@ -21,7 +21,7 @@ use lib_ws::WSConnectState;
 use std::sync::Arc;
 use tokio::sync::{mpsc, oneshot};
 
-pub struct ClientTextBlockEditor {
+pub struct TextBlockEditor {
     pub doc_id: String,
     #[allow(dead_code)]
     rev_manager: Arc<RevisionManager>,
@@ -30,7 +30,7 @@ pub struct ClientTextBlockEditor {
     edit_cmd_tx: EditorCommandSender,
 }
 
-impl ClientTextBlockEditor {
+impl TextBlockEditor {
     #[allow(unused_variables)]
     pub(crate) async fn new(
         doc_id: &str,
@@ -185,7 +185,7 @@ impl ClientTextBlockEditor {
     pub(crate) fn receive_ws_state(&self, _state: &WSConnectState) {}
 }
 
-impl std::ops::Drop for ClientTextBlockEditor {
+impl std::ops::Drop for TextBlockEditor {
     fn drop(&mut self) {
         tracing::trace!("{} ClientBlockEditor was dropped", self.doc_id)
     }
@@ -204,7 +204,7 @@ fn spawn_edit_queue(
 }
 
 #[cfg(feature = "flowy_unit_test")]
-impl ClientTextBlockEditor {
+impl TextBlockEditor {
     pub async fn text_block_delta(&self) -> FlowyResult<RichTextDelta> {
         let (ret, rx) = oneshot::channel::<CollaborateResult<RichTextDelta>>();
         let msg = EditorCommand::ReadDelta { ret };

+ 9 - 9
frontend/rust-lib/flowy-text-block/src/manager.rs

@@ -1,4 +1,4 @@
-use crate::{editor::ClientTextBlockEditor, errors::FlowyError, BlockCloudService};
+use crate::{editor::TextBlockEditor, errors::FlowyError, BlockCloudService};
 use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_database::ConnectionPool;
@@ -47,8 +47,8 @@ impl TextBlockManager {
         Ok(())
     }
 
-    #[tracing::instrument(level = "debug", skip(self, block_id), fields(block_id), err)]
-    pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
+    #[tracing::instrument(level = "trace", skip(self, block_id), fields(block_id), err)]
+    pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<TextBlockEditor>, FlowyError> {
         let block_id = block_id.as_ref();
         tracing::Span::current().record("block_id", &block_id);
         self.get_block_editor(block_id).await
@@ -108,7 +108,7 @@ impl TextBlockManager {
 }
 
 impl TextBlockManager {
-    async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientTextBlockEditor>> {
+    async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<TextBlockEditor>> {
         match self.editor_map.get(block_id) {
             None => {
                 let db_pool = self.user.db_pool()?;
@@ -123,7 +123,7 @@ impl TextBlockManager {
         &self,
         block_id: &str,
         pool: Arc<ConnectionPool>,
-    ) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
+    ) -> Result<Arc<TextBlockEditor>, FlowyError> {
         let user = self.user.clone();
         let token = self.user.token()?;
         let rev_manager = self.make_rev_manager(block_id, pool.clone())?;
@@ -132,7 +132,7 @@ impl TextBlockManager {
             server: self.cloud_service.clone(),
         });
         let doc_editor =
-            ClientTextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
+            TextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
         self.editor_map.insert(block_id, &doc_editor);
         Ok(doc_editor)
     }
@@ -180,7 +180,7 @@ impl RevisionCloudService for TextBlockRevisionCloudService {
 }
 
 pub struct TextBlockEditorMap {
-    inner: DashMap<String, Arc<ClientTextBlockEditor>>,
+    inner: DashMap<String, Arc<TextBlockEditor>>,
 }
 
 impl TextBlockEditorMap {
@@ -188,14 +188,14 @@ impl TextBlockEditorMap {
         Self { inner: DashMap::new() }
     }
 
-    pub(crate) fn insert(&self, block_id: &str, doc: &Arc<ClientTextBlockEditor>) {
+    pub(crate) fn insert(&self, block_id: &str, doc: &Arc<TextBlockEditor>) {
         if self.inner.contains_key(block_id) {
             log::warn!("Doc:{} already exists in cache", block_id);
         }
         self.inner.insert(block_id.to_string(), doc.clone());
     }
 
-    pub(crate) fn get(&self, block_id: &str) -> Option<Arc<ClientTextBlockEditor>> {
+    pub(crate) fn get(&self, block_id: &str) -> Option<Arc<TextBlockEditor>> {
         Some(self.inner.get(block_id)?.clone())
     }
 

+ 2 - 2
frontend/rust-lib/flowy-text-block/tests/document/script.rs

@@ -1,6 +1,6 @@
 use flowy_revision::disk::RevisionState;
 use flowy_test::{helper::ViewTest, FlowySDKTest};
-use flowy_text_block::editor::ClientTextBlockEditor;
+use flowy_text_block::editor::TextBlockEditor;
 use flowy_text_block::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS;
 use lib_ot::{core::Interval, rich_text::RichTextDelta};
 use std::sync::Arc;
@@ -19,7 +19,7 @@ pub enum EditorScript {
 
 pub struct TextBlockEditorTest {
     pub sdk: FlowySDKTest,
-    pub editor: Arc<ClientTextBlockEditor>,
+    pub editor: Arc<TextBlockEditor>,
 }
 
 impl TextBlockEditorTest {

+ 12 - 24
frontend/scripts/makefile/tests.toml

@@ -1,30 +1,18 @@
 
-[tasks.test_local]
-category = "Build"
-dependencies = ["rm_cache"]
-description = "Build desktop targets."
+[tasks.rust_unit_test]
+run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] }
+
+[tasks.rust_lib_unit_test]
+description = "Run rust-lib unit tests"
 script = '''
 cd rust-lib
-cargo test
+RUST_LOG=info cargo test --no-default-features --features="sync"
 '''
 
+[tasks.shared_lib_unit_test]
+description = "Run shared-lib unit test"
+script = '''
+cd ../shared-lib
+RUST_LOG=info cargo test --no-default-features
+'''
 
-[tasks.test_remote]
-dependencies = ["rm_cache"]
-script = """
-cd rust-lib
-cargo test --features "flowy-folder/http_server","flowy-user/http_server"
-"""
-
-
-[tasks.run_server]
-script = """
-cd backend
-cargo run
-"""
-
-
-[tasks.rm_cache]
-script = """
-rm -rf rust-lib/flowy-test/temp
-"""

+ 27 - 0
frontend/scripts/makefile/tool.toml

@@ -0,0 +1,27 @@
+[tasks.rust_clean]
+script = [
+  """
+    cd rust-lib
+    cargo clean
+
+    cd ../../shared-lib
+    cargo clean
+
+    rm -rf lib-infra/.cache
+  """,
+]
+script_runner = "@shell"
+
+[tasks.rust_clean.windows]
+script = [
+  """
+    cd rust-lib
+    cargo clean
+
+    cd ../../shared-lib
+    cargo clean
+
+    rmdir /s/q "lib-infra/.cache"
+  """,
+]
+script_runner = "@duckscript"

+ 8 - 0
shared-lib/flowy-derive/src/proto_buf/deserialize.rs

@@ -30,6 +30,14 @@ pub fn make_de_token_steam(ctxt: &Ctxt, ast: &ASTContainer) -> Option<TokenStrea
             }
         }
 
+        impl std::convert::TryFrom<&[u8]> for #struct_ident {
+            type Error = ::protobuf::ProtobufError;
+            fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
+                let pb: crate::protobuf::#pb_ty = ::protobuf::Message::parse_from_bytes(bytes)?;
+                #struct_ident::try_from(pb)
+            }
+        }
+
         impl std::convert::TryFrom<crate::protobuf::#pb_ty> for #struct_ident {
             type Error = ::protobuf::ProtobufError;
             fn try_from(mut pb: crate::protobuf::#pb_ty) -> Result<Self, Self::Error> {

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

@@ -484,17 +484,13 @@ pub struct Cell {
     pub field_id: String,
 
     #[pb(index = 2)]
-    pub content: String,
-
-    #[pb(index = 3)]
-    pub data: String,
+    pub data: Vec<u8>,
 }
 
 impl Cell {
-    pub fn new(field_id: &str, content: String, data: String) -> Self {
+    pub fn new(field_id: &str, data: Vec<u8>) -> Self {
         Self {
             field_id: field_id.to_owned(),
-            content,
             data,
         }
     }
@@ -502,8 +498,7 @@ impl Cell {
     pub fn empty(field_id: &str) -> Self {
         Self {
             field_id: field_id.to_owned(),
-            content: "".to_string(),
-            data: "".to_string(),
+            data: vec![],
         }
     }
 }

+ 54 - 97
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -4743,8 +4743,7 @@ impl ::protobuf::reflect::ProtobufValue for GridBlock {
 pub struct Cell {
     // message fields
     pub field_id: ::std::string::String,
-    pub content: ::std::string::String,
-    pub data: ::std::string::String,
+    pub data: ::std::vec::Vec<u8>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -4787,36 +4786,10 @@ impl Cell {
         ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
     }
 
-    // string content = 2;
+    // bytes data = 2;
 
 
-    pub fn get_content(&self) -> &str {
-        &self.content
-    }
-    pub fn clear_content(&mut self) {
-        self.content.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_content(&mut self, v: ::std::string::String) {
-        self.content = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_content(&mut self) -> &mut ::std::string::String {
-        &mut self.content
-    }
-
-    // Take field
-    pub fn take_content(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.content, ::std::string::String::new())
-    }
-
-    // string data = 3;
-
-
-    pub fn get_data(&self) -> &str {
+    pub fn get_data(&self) -> &[u8] {
         &self.data
     }
     pub fn clear_data(&mut self) {
@@ -4824,19 +4797,19 @@ impl Cell {
     }
 
     // Param is passed by value, moved
-    pub fn set_data(&mut self, v: ::std::string::String) {
+    pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
         self.data = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_data(&mut self) -> &mut ::std::string::String {
+    pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
         &mut self.data
     }
 
     // Take field
-    pub fn take_data(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.data, ::std::string::String::new())
+    pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
+        ::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
     }
 }
 
@@ -4853,10 +4826,7 @@ impl ::protobuf::Message for Cell {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?;
-                },
-                3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?;
+                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -4873,11 +4843,8 @@ impl ::protobuf::Message for Cell {
         if !self.field_id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.field_id);
         }
-        if !self.content.is_empty() {
-            my_size += ::protobuf::rt::string_size(2, &self.content);
-        }
         if !self.data.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.data);
+            my_size += ::protobuf::rt::bytes_size(2, &self.data);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -4888,11 +4855,8 @@ impl ::protobuf::Message for Cell {
         if !self.field_id.is_empty() {
             os.write_string(1, &self.field_id)?;
         }
-        if !self.content.is_empty() {
-            os.write_string(2, &self.content)?;
-        }
         if !self.data.is_empty() {
-            os.write_string(3, &self.data)?;
+            os.write_bytes(2, &self.data)?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -4937,12 +4901,7 @@ impl ::protobuf::Message for Cell {
                 |m: &Cell| { &m.field_id },
                 |m: &mut Cell| { &mut m.field_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "content",
-                |m: &Cell| { &m.content },
-                |m: &mut Cell| { &mut m.content },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "data",
                 |m: &Cell| { &m.data },
                 |m: &mut Cell| { &mut m.data },
@@ -4964,7 +4923,6 @@ impl ::protobuf::Message for Cell {
 impl ::protobuf::Clear for Cell {
     fn clear(&mut self) {
         self.field_id.clear();
-        self.content.clear();
         self.data.clear();
         self.unknown_fields.clear();
     }
@@ -8343,50 +8301,49 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     derR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\x10.Upd\
     atedRowOrderR\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\trow\
-    Orders\"O\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\
-    \x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\x12\x12\n\x04data\
-    \x18\x03\x20\x01(\tR\x04data\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\
-    \x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\
-    \n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05va\
-    lue\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\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12Ins\
-    ertFieldPayload\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_fie\
-    ld_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_fiel\
-    d_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\x07grid_id\x18\x01\
-    \x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fiel\
-    dId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"\
-    d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gri\
-    dId\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\
-    \"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06\
-    gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08fie\
-    ld_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content_changeset\
-    \x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_of_cell_co\
-    ntent_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\x12\x0b\n\
-    \x07MoveRow\x10\x01*m\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\
-    \x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSele\
-    ct\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\
-    \x05\x12\x07\n\x03URL\x10\x06b\x06proto3\
+    Orders\"5\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\
+    \x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\"+\n\x0cRepeatedCell\
+    \x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11Cre\
+    ateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06\
+    GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlock\
+    Id\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayl\
+    oad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_\
+    row_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(\
+    \tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05fie\
+    ld\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\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\
+    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\
+    \x20\x01(\tR\x07fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\
+    \x0etypeOptionData\"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\"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\
+    \x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05ro\
+    wId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_\
+    content_changeset\x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\
+    \x1done_of_cell_content_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\
+    \x10\0\x12\x0b\n\x07MoveRow\x10\x01*m\n\tFieldType\x12\x0c\n\x08RichText\
+    \x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\
+    \x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08C\
+    heckbox\x10\x05\x12\x07\n\x03URL\x10\x06b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -95,8 +95,7 @@ message GridBlock {
 }
 message Cell {
     string field_id = 1;
-    string content = 2;
-    string data = 3;
+    bytes data = 2;
 }
 message RepeatedCell {
     repeated Cell items = 1;

+ 1 - 1
shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs

@@ -175,7 +175,7 @@ impl GridBlockMetaPad {
                 match cal_diff::<PlainTextAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
-                        tracing::debug!("[GridBlockMeta] Composing delta {}", delta.to_delta_str());
+                        tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_delta_str());
                         // tracing::debug!(
                         //     "[GridBlockMeta] current delta: {}",
                         //     self.delta.to_str().unwrap_or_else(|_| "".to_string())