Explorar o código

fix: unit test errors

appflowy %!s(int64=3) %!d(string=hai) anos
pai
achega
a3770a699c
Modificáronse 29 ficheiros con 468 adicións e 1511 borrados
  1. 1 1
      frontend/app_flowy/lib/user/presentation/splash_screen.dart
  2. 1 1
      frontend/app_flowy/lib/workspace/application/home/home_listen_bloc.dart
  3. 17 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart
  4. 0 11
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pb.dart
  5. 0 56
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbenum.dart
  6. 0 36
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbjson.dart
  7. 0 9
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbserver.dart
  8. 1 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart
  9. 1 1
      frontend/rust-lib/flowy-grid/src/macros.rs
  10. 10 0
      frontend/rust-lib/flowy-grid/src/manager.rs
  11. 4 251
      frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs
  12. 248 0
      frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs
  13. 18 12
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  14. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs
  15. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs
  16. 94 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs
  17. 9 13
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  18. 3 3
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  19. 1 0
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  20. 17 0
      frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs
  21. 1 1
      frontend/rust-lib/flowy-grid/src/services/row/row_entities.rs
  22. 12 1
      frontend/rust-lib/flowy-grid/src/util.rs
  23. 13 74
      frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs
  24. 11 8
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  25. 2 1
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  26. 1 1
      shared-lib/flowy-grid-data-model/src/entities/meta.rs
  27. 0 3
      shared-lib/flowy-grid-data-model/src/protobuf/model/mod.rs
  28. 0 978
      shared-lib/flowy-grid-data-model/src/protobuf/model/type_option.rs
  29. 0 29
      shared-lib/flowy-grid-data-model/src/protobuf/proto/type_option.proto

+ 1 - 1
frontend/app_flowy/lib/user/presentation/splash_screen.dart

@@ -4,7 +4,7 @@ import 'package:app_flowy/user/domain/auth_state.dart';
 import 'package:app_flowy/user/presentation/router.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
-import 'package:flowy_sdk/protobuf/flowy-folder-data-model/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/error-code/error_code.pbenum.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 1 - 1
frontend/app_flowy/lib/workspace/application/home/home_listen_bloc.dart

@@ -1,5 +1,5 @@
 import 'package:app_flowy/user/application/user_listener.dart';
-import 'package:flowy_sdk/protobuf/flowy-folder-data-model/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/error-code/error_code.pbenum.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 17 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -72,13 +72,28 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData);
+    _cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData)..add(const SelectionCellEvent.initial());
     super.initState();
   }
 
   @override
   Widget build(BuildContext context) {
-    return Container();
+    return BlocProvider.value(
+      value: _cellBloc,
+      child: BlocBuilder<SelectionCellBloc, SelectionCellState>(
+        builder: (context, state) {
+          final children = state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList();
+          return SizedBox.expand(
+            child: InkWell(
+              onTap: () {
+                SelectOptionEditor.show(context, state.cellData, state.options, state.selectedOptions);
+              },
+              child: Row(children: children),
+            ),
+          );
+        },
+      ),
+    );
   }
 
   @override

+ 0 - 11
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pb.dart

@@ -1,11 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: errors.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-
-import 'dart:core' as $core;
-
-export 'errors.pbenum.dart';
-

+ 0 - 56
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbenum.dart

@@ -1,56 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: errors.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-
-// ignore_for_file: UNDEFINED_SHOWN_NAME
-import 'dart:core' as $core;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class ErrorCode extends $pb.ProtobufEnum {
-  static const ErrorCode WorkspaceNameInvalid = ErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameInvalid');
-  static const ErrorCode WorkspaceIdInvalid = ErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceIdInvalid');
-  static const ErrorCode AppColorStyleInvalid = ErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppColorStyleInvalid');
-  static const ErrorCode WorkspaceDescTooLong = ErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDescTooLong');
-  static const ErrorCode WorkspaceNameTooLong = ErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameTooLong');
-  static const ErrorCode AppIdInvalid = ErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppIdInvalid');
-  static const ErrorCode AppNameInvalid = ErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppNameInvalid');
-  static const ErrorCode ViewNameInvalid = ErrorCode._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewNameInvalid');
-  static const ErrorCode ViewThumbnailInvalid = ErrorCode._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewThumbnailInvalid');
-  static const ErrorCode ViewIdInvalid = ErrorCode._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewIdInvalid');
-  static const ErrorCode ViewDescTooLong = ErrorCode._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDescTooLong');
-  static const ErrorCode ViewDataInvalid = ErrorCode._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDataInvalid');
-  static const ErrorCode ViewNameTooLong = ErrorCode._(25, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewNameTooLong');
-  static const ErrorCode UserUnauthorized = ErrorCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
-  static const ErrorCode WsConnectError = ErrorCode._(200, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WsConnectError');
-  static const ErrorCode InternalError = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InternalError');
-  static const ErrorCode RecordNotFound = ErrorCode._(1001, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RecordNotFound');
-
-  static const $core.List<ErrorCode> values = <ErrorCode> [
-    WorkspaceNameInvalid,
-    WorkspaceIdInvalid,
-    AppColorStyleInvalid,
-    WorkspaceDescTooLong,
-    WorkspaceNameTooLong,
-    AppIdInvalid,
-    AppNameInvalid,
-    ViewNameInvalid,
-    ViewThumbnailInvalid,
-    ViewIdInvalid,
-    ViewDescTooLong,
-    ViewDataInvalid,
-    ViewNameTooLong,
-    UserUnauthorized,
-    WsConnectError,
-    InternalError,
-    RecordNotFound,
-  ];
-
-  static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static ErrorCode? valueOf($core.int value) => _byValue[value];
-
-  const ErrorCode._($core.int v, $core.String n) : super(v, n);
-}
-

+ 0 - 36
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbjson.dart

@@ -1,36 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: errors.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use errorCodeDescriptor instead')
-const ErrorCode$json = const {
-  '1': 'ErrorCode',
-  '2': const [
-    const {'1': 'WorkspaceNameInvalid', '2': 0},
-    const {'1': 'WorkspaceIdInvalid', '2': 1},
-    const {'1': 'AppColorStyleInvalid', '2': 2},
-    const {'1': 'WorkspaceDescTooLong', '2': 3},
-    const {'1': 'WorkspaceNameTooLong', '2': 4},
-    const {'1': 'AppIdInvalid', '2': 10},
-    const {'1': 'AppNameInvalid', '2': 11},
-    const {'1': 'ViewNameInvalid', '2': 20},
-    const {'1': 'ViewThumbnailInvalid', '2': 21},
-    const {'1': 'ViewIdInvalid', '2': 22},
-    const {'1': 'ViewDescTooLong', '2': 23},
-    const {'1': 'ViewDataInvalid', '2': 24},
-    const {'1': 'ViewNameTooLong', '2': 25},
-    const {'1': 'UserUnauthorized', '2': 100},
-    const {'1': 'WsConnectError', '2': 200},
-    const {'1': 'InternalError', '2': 1000},
-    const {'1': 'RecordNotFound', '2': 1001},
-  ],
-};
-
-/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQABIWChJXb3Jrc3BhY2VJZEludmFsaWQQARIYChRBcHBDb2xvclN0eWxlSW52YWxpZBACEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEAMSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQBBIQCgxBcHBJZEludmFsaWQQChISCg5BcHBOYW1lSW52YWxpZBALEhMKD1ZpZXdOYW1lSW52YWxpZBAUEhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEBUSEQoNVmlld0lkSW52YWxpZBAWEhMKD1ZpZXdEZXNjVG9vTG9uZxAXEhMKD1ZpZXdEYXRhSW52YWxpZBAYEhMKD1ZpZXdOYW1lVG9vTG9uZxAZEhQKEFVzZXJVbmF1dGhvcml6ZWQQZBITCg5Xc0Nvbm5lY3RFcnJvchDIARISCg1JbnRlcm5hbEVycm9yEOgHEhMKDlJlY29yZE5vdEZvdW5kEOkH');

+ 0 - 9
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/errors.pbserver.dart

@@ -1,9 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: errors.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-export 'errors.pb.dart';
-

+ 1 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart

@@ -1,3 +1,3 @@
-// Auto-generated, do not edit
+// Auto-generated, do not edit 
 export './grid.pb.dart';
 export './meta.pb.dart';

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

@@ -52,7 +52,7 @@ macro_rules! impl_type_option {
                 match serde_json::to_string(&self) {
                     Ok(s) => s,
                     Err(e) => {
-                        tracing::error!("Field type data convert to AnyData fail, error: {:?}", e);
+                        tracing::error!("Field type data serialize to json fail, error: {:?}", e);
                         serde_json::to_string(&$target::default()).unwrap()
                     }
                 }

+ 10 - 0
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -23,6 +23,7 @@ pub struct GridManager {
     editor_map: Arc<GridEditorMap>,
     grid_user: Arc<dyn GridUser>,
     block_index_persistence: Arc<BlockIndexPersistence>,
+    #[allow(dead_code)]
     kv_persistence: Arc<GridKVPersistence>,
 }
 
@@ -181,12 +182,21 @@ pub async fn make_grid_view_data(
         block_metas: vec![build_context.block_metas],
     };
 
+    // Create grid
     let grid_meta_delta = make_grid_delta(&grid_meta);
     let grid_delta_data = grid_meta_delta.to_delta_bytes();
     let repeated_revision: RepeatedRevision =
         Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
     let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
 
+    // Indexing the block's rows
+    build_context.block_meta_data.row_metas.iter().for_each(|row| {
+        let _ = grid_manager
+            .block_index_persistence
+            .insert_or_update(&row.block_id, &row.id);
+    });
+
+    // Create grid's block
     let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
     let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
     let repeated_revision: RepeatedRevision =

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

@@ -1,263 +1,16 @@
-use crate::dart_notification::{send_dart_notification, GridNotification};
-use crate::manager::GridUser;
-use crate::services::persistence::block_index::BlockIndexPersistence;
-use crate::services::row::{make_block_row_ids, make_cell_by_field_id, make_rows_from_row_metas, GridBlockSnapshot};
 use bytes::Bytes;
-use dashmap::DashMap;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::entities::{
-    CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
-    GridBlockOrder, RepeatedCell, Row, RowMeta, RowMetaChangeset, RowOrder,
-};
-use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
-use flowy_revision::{
-    RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
-};
+use flowy_grid_data_model::entities::{CellMeta, RowMeta, RowMetaChangeset, RowOrder};
+use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
 use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockMetaPad};
 use flowy_sync::entities::revision::Revision;
 use flowy_sync::util::make_delta_from_revisions;
 use lib_infra::future::FutureResult;
 use lib_ot::core::PlainTextAttributes;
-use std::collections::HashMap;
+
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
-pub(crate) struct GridBlockMetaEditorManager {
-    grid_id: String,
-    user: Arc<dyn GridUser>,
-    editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
-    persistence: Arc<BlockIndexPersistence>,
-}
-
-impl GridBlockMetaEditorManager {
-    pub(crate) async fn new(
-        grid_id: &str,
-        user: &Arc<dyn GridUser>,
-        blocks: Vec<GridBlockMeta>,
-        persistence: Arc<BlockIndexPersistence>,
-    ) -> FlowyResult<Self> {
-        let editor_map = make_block_meta_editor_map(user, blocks).await?;
-        let user = user.clone();
-        let grid_id = grid_id.to_owned();
-        let manager = Self {
-            grid_id,
-            user,
-            editor_map,
-            persistence,
-        };
-        Ok(manager)
-    }
-
-    // #[tracing::instrument(level = "trace", skip(self))]
-    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
-        debug_assert!(!block_id.is_empty());
-        match self.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());
-                Ok(editor)
-            }
-            Some(editor) => Ok(editor.clone()),
-        }
-    }
-
-    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
-        let block_id = self.persistence.get_block_id(row_id)?;
-        Ok(self.get_editor(&block_id).await?)
-    }
-
-    pub(crate) async fn create_row(
-        &self,
-        block_id: &str,
-        row_meta: RowMeta,
-        start_row_id: Option<String>,
-    ) -> FlowyResult<i32> {
-        let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
-        let editor = self.get_editor(&row_meta.block_id).await?;
-        let row_count = editor.create_row(row_meta, start_row_id).await?;
-        self.notify_block_did_update_row(block_id).await?;
-        Ok(row_count)
-    }
-
-    pub(crate) async fn insert_row(
-        &self,
-        rows_by_block_id: HashMap<String, Vec<RowMeta>>,
-    ) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
-        let mut changesets = vec![];
-        for (block_id, row_metas) in rows_by_block_id {
-            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)?;
-                row_count = editor.create_row(row.clone(), None).await?;
-            }
-            changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
-            let _ = self.notify_block_did_update_row(&block_id).await?;
-        }
-
-        Ok(changesets)
-    }
-
-    pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
-        let mut changesets = vec![];
-        for block_row_ids in make_block_row_ids(&row_orders) {
-            let editor = self.get_editor(&block_row_ids.block_id).await?;
-            let row_count = editor.delete_rows(block_row_ids.row_ids).await?;
-
-            let changeset = GridBlockMetaChangeset::from_row_count(&block_row_ids.block_id, row_count);
-            changesets.push(changeset);
-        }
-
-        Ok(changesets)
-    }
-
-    pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
-        let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
-        let _ = editor.update_row(changeset.clone()).await?;
-        let _ = self.notify_block_did_update_row(&editor.block_id).await?;
-        Ok(())
-    }
-
-    pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
-        let row_id = changeset.row_id.clone();
-        let editor = self.get_editor_from_row_id(&row_id).await?;
-        let row_changeset: RowMetaChangeset = changeset.clone().into();
-        let _ = editor.update_row(row_changeset).await?;
-
-        let cell_notification_data = CellNotificationData {
-            grid_id: changeset.grid_id,
-            field_id: changeset.field_id,
-            row_id: changeset.row_id,
-            content: changeset.data,
-        };
-        self.notify_did_update_cell(cell_notification_data).await?;
-        Ok(())
-    }
-
-    pub async fn get_row_meta(&self, row_id: &str) -> FlowyResult<Option<Arc<RowMeta>>> {
-        let editor = self.get_editor_from_row_id(row_id).await?;
-        let row_ids = vec![row_id.to_owned()];
-        let mut row_metas = editor.get_row_metas(Some(row_ids)).await?;
-        if row_metas.is_empty() {
-            Ok(None)
-        } else {
-            Ok(row_metas.pop())
-        }
-    }
-
-    pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
-        let mut snapshots = vec![];
-        for block_id in block_ids {
-            let editor = self.get_editor(&block_id).await?;
-            let row_metas = editor.get_row_metas(None).await?;
-
-            row_metas.iter().for_each(|row| {
-                let _ = self.persistence.insert_or_update(&row.block_id, &row.id);
-            });
-
-            snapshots.push(GridBlockSnapshot { block_id, row_metas });
-        }
-        Ok(snapshots)
-    }
-
-    // Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
-    #[allow(dead_code)]
-    pub async fn get_cell_metas(
-        &self,
-        block_ids: Vec<String>,
-        field_id: &str,
-        row_ids: Option<Vec<String>>,
-    ) -> FlowyResult<Vec<CellMeta>> {
-        let mut block_cell_metas = vec![];
-        for block_id in block_ids {
-            let editor = self.get_editor(&block_id).await?;
-            let cell_metas = editor.get_cell_metas(field_id, &row_ids).await?;
-            block_cell_metas.extend(cell_metas);
-        }
-        Ok(block_cell_metas)
-    }
-
-    async fn notify_block_did_update_row(&self, block_id: &str) -> FlowyResult<()> {
-        let block_order: GridBlockOrder = block_id.into();
-        send_dart_notification(&self.grid_id, GridNotification::DidUpdateBlock)
-            .payload(block_order)
-            .send();
-        Ok(())
-    }
-
-    async fn notify_did_update_cell(&self, data: CellNotificationData) -> FlowyResult<()> {
-        let id = format!("{}:{}", data.row_id, data.field_id);
-        send_dart_notification(&id, GridNotification::DidUpdateCell)
-            .payload(data)
-            .send();
-        Ok(())
-    }
-
-    async fn notify_did_update_row(&self, row_id: &str, field_metas: &[FieldMeta]) -> FlowyResult<()> {
-        match self.get_row_meta(row_id).await? {
-            None => {}
-            Some(row_meta) => {
-                let row_metas = vec![row_meta];
-                if let Some(row) = make_rows_from_row_metas(&field_metas, &row_metas).pop() {
-                    send_dart_notification(row_id, GridNotification::DidUpdateRow)
-                        .payload(row)
-                        .send();
-                }
-            }
-        }
-        Ok(())
-
-        //
-        // let field_meta_map = field_metas
-        //     .iter()
-        //     .map(|field_meta| (&field_meta.id, field_meta))
-        //     .collect::<HashMap<&String, &FieldMeta>>();
-        //
-        // let mut cells = vec![];
-        // changeset
-        //     .cell_by_field_id
-        //     .into_iter()
-        //     .for_each(
-        //         |(field_id, cell_meta)| match make_cell_by_field_id(&field_meta_map, field_id, cell_meta) {
-        //             None => {}
-        //             Some((_, cell)) => cells.push(cell),
-        //         },
-        //     );
-        //
-        // if !cells.is_empty() {
-        //     send_dart_notification(&changeset.row_id, GridNotification::DidUpdateRow)
-        //         .payload(RepeatedCell::from(cells))
-        //         .send();
-        // }
-        // Ok(())
-    }
-}
-
-async fn make_block_meta_editor_map(
-    user: &Arc<dyn GridUser>,
-    blocks: Vec<GridBlockMeta>,
-) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
-    let editor_map = DashMap::new();
-    for block in blocks {
-        let editor = make_block_meta_editor(user, &block.block_id).await?;
-        editor_map.insert(block.block_id, Arc::new(editor));
-    }
-
-    Ok(editor_map)
-}
-
-async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
-    let token = user.token()?;
-    let user_id = user.user_id()?;
-    let pool = user.db_pool()?;
-
-    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
-}
-
 pub struct ClientGridBlockMetaEditor {
     user_id: String,
     pub block_id: String,
@@ -288,7 +41,7 @@ impl ClientGridBlockMetaEditor {
         })
     }
 
-    async fn create_row(&self, row: RowMeta, start_row_id: Option<String>) -> FlowyResult<i32> {
+    pub(crate) async fn create_row(&self, row: RowMeta, start_row_id: Option<String>) -> FlowyResult<i32> {
         let mut row_count = 0;
         let _ = self
             .modify(|pad| {

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

@@ -0,0 +1,248 @@
+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::row::{make_block_row_ids, make_rows_from_row_metas, GridBlockSnapshot};
+
+use dashmap::DashMap;
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::entities::{
+    CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
+    GridBlockOrder, RowMeta, RowMetaChangeset, RowOrder,
+};
+use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
+use flowy_revision::{RevisionManager, RevisionPersistence};
+use std::collections::HashMap;
+use std::sync::Arc;
+
+pub(crate) struct GridBlockMetaEditorManager {
+    grid_id: String,
+    user: Arc<dyn GridUser>,
+    editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
+    persistence: Arc<BlockIndexPersistence>,
+}
+
+impl GridBlockMetaEditorManager {
+    pub(crate) async fn new(
+        grid_id: &str,
+        user: &Arc<dyn GridUser>,
+        blocks: Vec<GridBlockMeta>,
+        persistence: Arc<BlockIndexPersistence>,
+    ) -> FlowyResult<Self> {
+        let editor_map = make_block_meta_editor_map(user, blocks).await?;
+        let user = user.clone();
+        let grid_id = grid_id.to_owned();
+        let manager = Self {
+            grid_id,
+            user,
+            editor_map,
+            persistence,
+        };
+        Ok(manager)
+    }
+
+    // #[tracing::instrument(level = "trace", skip(self))]
+    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
+        debug_assert!(!block_id.is_empty());
+        match self.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());
+                Ok(editor)
+            }
+            Some(editor) => Ok(editor.clone()),
+        }
+    }
+
+    async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
+        let block_id = self.persistence.get_block_id(row_id)?;
+        Ok(self.get_editor(&block_id).await?)
+    }
+
+    pub(crate) async fn create_row(
+        &self,
+        block_id: &str,
+        row_meta: RowMeta,
+        start_row_id: Option<String>,
+    ) -> FlowyResult<i32> {
+        let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
+        let editor = self.get_editor(&row_meta.block_id).await?;
+        let row_count = editor.create_row(row_meta, start_row_id).await?;
+        self.notify_block_did_update_row(block_id).await?;
+        Ok(row_count)
+    }
+
+    pub(crate) async fn insert_row(
+        &self,
+        rows_by_block_id: HashMap<String, Vec<RowMeta>>,
+    ) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
+        let mut changesets = vec![];
+        for (block_id, row_metas) in rows_by_block_id {
+            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)?;
+                row_count = editor.create_row(row.clone(), None).await?;
+            }
+            changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
+            let _ = self.notify_block_did_update_row(&block_id).await?;
+        }
+
+        Ok(changesets)
+    }
+
+    pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
+        let mut changesets = vec![];
+        for block_row_ids in make_block_row_ids(&row_orders) {
+            let editor = self.get_editor(&block_row_ids.block_id).await?;
+            let row_count = editor.delete_rows(block_row_ids.row_ids).await?;
+
+            let changeset = GridBlockMetaChangeset::from_row_count(&block_row_ids.block_id, row_count);
+            changesets.push(changeset);
+        }
+
+        Ok(changesets)
+    }
+
+    pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
+        let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
+        let _ = editor.update_row(changeset.clone()).await?;
+        let _ = self.notify_block_did_update_row(&editor.block_id).await?;
+        Ok(())
+    }
+
+    pub async fn update_cell(&self, changeset: CellMetaChangeset) -> FlowyResult<()> {
+        let row_id = changeset.row_id.clone();
+        let editor = self.get_editor_from_row_id(&row_id).await?;
+        let row_changeset: RowMetaChangeset = changeset.clone().into();
+        let _ = editor.update_row(row_changeset).await?;
+
+        let cell_notification_data = CellNotificationData {
+            grid_id: changeset.grid_id,
+            field_id: changeset.field_id,
+            row_id: changeset.row_id,
+            content: changeset.data,
+        };
+        self.notify_did_update_cell(cell_notification_data).await?;
+        Ok(())
+    }
+
+    pub async fn get_row_meta(&self, row_id: &str) -> FlowyResult<Option<Arc<RowMeta>>> {
+        let editor = self.get_editor_from_row_id(row_id).await?;
+        let row_ids = vec![row_id.to_owned()];
+        let mut row_metas = editor.get_row_metas(Some(row_ids)).await?;
+        if row_metas.is_empty() {
+            Ok(None)
+        } else {
+            Ok(row_metas.pop())
+        }
+    }
+
+    pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
+        let mut snapshots = vec![];
+        for block_id in block_ids {
+            let editor = self.get_editor(&block_id).await?;
+            let row_metas = editor.get_row_metas(None).await?;
+            snapshots.push(GridBlockSnapshot { block_id, row_metas });
+        }
+        Ok(snapshots)
+    }
+
+    // Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
+    #[allow(dead_code)]
+    pub async fn get_cell_metas(
+        &self,
+        block_ids: Vec<String>,
+        field_id: &str,
+        row_ids: Option<Vec<String>>,
+    ) -> FlowyResult<Vec<CellMeta>> {
+        let mut block_cell_metas = vec![];
+        for block_id in block_ids {
+            let editor = self.get_editor(&block_id).await?;
+            let cell_metas = editor.get_cell_metas(field_id, &row_ids).await?;
+            block_cell_metas.extend(cell_metas);
+        }
+        Ok(block_cell_metas)
+    }
+
+    async fn notify_block_did_update_row(&self, block_id: &str) -> FlowyResult<()> {
+        let block_order: GridBlockOrder = block_id.into();
+        send_dart_notification(&self.grid_id, GridNotification::DidUpdateBlock)
+            .payload(block_order)
+            .send();
+        Ok(())
+    }
+
+    async fn notify_did_update_cell(&self, data: CellNotificationData) -> FlowyResult<()> {
+        let id = format!("{}:{}", data.row_id, data.field_id);
+        send_dart_notification(&id, GridNotification::DidUpdateCell)
+            .payload(data)
+            .send();
+        Ok(())
+    }
+
+    #[allow(dead_code)]
+    async fn notify_did_update_row(&self, row_id: &str, field_metas: &[FieldMeta]) -> FlowyResult<()> {
+        match self.get_row_meta(row_id).await? {
+            None => {}
+            Some(row_meta) => {
+                let row_metas = vec![row_meta];
+                if let Some(row) = make_rows_from_row_metas(field_metas, &row_metas).pop() {
+                    send_dart_notification(row_id, GridNotification::DidUpdateRow)
+                        .payload(row)
+                        .send();
+                }
+            }
+        }
+        Ok(())
+
+        //
+        // let field_meta_map = field_metas
+        //     .iter()
+        //     .map(|field_meta| (&field_meta.id, field_meta))
+        //     .collect::<HashMap<&String, &FieldMeta>>();
+        //
+        // let mut cells = vec![];
+        // changeset
+        //     .cell_by_field_id
+        //     .into_iter()
+        //     .for_each(
+        //         |(field_id, cell_meta)| match make_cell_by_field_id(&field_meta_map, field_id, cell_meta) {
+        //             None => {}
+        //             Some((_, cell)) => cells.push(cell),
+        //         },
+        //     );
+        //
+        // if !cells.is_empty() {
+        //     send_dart_notification(&changeset.row_id, GridNotification::DidUpdateRow)
+        //         .payload(RepeatedCell::from(cells))
+        //         .send();
+        // }
+        // Ok(())
+    }
+}
+
+async fn make_block_meta_editor_map(
+    user: &Arc<dyn GridUser>,
+    blocks: Vec<GridBlockMeta>,
+) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
+    let editor_map = DashMap::new();
+    for block in blocks {
+        let editor = make_block_meta_editor(user, &block.block_id).await?;
+        editor_map.insert(block.block_id, Arc::new(editor));
+    }
+
+    Ok(editor_map)
+}
+
+async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
+    let token = user.token()?;
+    let user_id = user.user_id()?;
+    let pool = user.db_pool()?;
+
+    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
+}

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

@@ -43,7 +43,7 @@ const NO: &str = "No";
 impl CellDataOperation for CheckboxTypeOption {
     fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
-            if !type_option_cell_data.is_text() || !type_option_cell_data.is_checkbox() {
+            if !type_option_cell_data.is_checkbox() {
                 return String::new();
             }
             let cell_data = type_option_cell_data.data;
@@ -58,7 +58,7 @@ impl CellDataOperation for CheckboxTypeOption {
     fn apply_changeset<T: Into<CellDataChangeset>>(
         &self,
         changeset: T,
-        cell_meta: Option<CellMeta>,
+        _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
         let s = match string_to_bool(&changeset) {
@@ -84,6 +84,7 @@ fn string_to_bool(bool_str: &str) -> bool {
 
 #[cfg(test)]
 mod tests {
+    use crate::services::field::type_options::checkbox_type_option::{NO, YES};
     use crate::services::field::CheckboxTypeOption;
     use crate::services::field::FieldBuilder;
     use crate::services::row::CellDataOperation;
@@ -94,17 +95,22 @@ mod tests {
         let type_option = CheckboxTypeOption::default();
         let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
 
-        assert_eq!(type_option.apply_changeset("true").unwrap(), "1".to_owned());
-        assert_eq!(type_option.apply_changeset("1").unwrap(), "1".to_owned());
-        assert_eq!(type_option.apply_changeset("yes").unwrap(), "1".to_owned());
+        let data = type_option.apply_changeset("true", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
 
-        assert_eq!(type_option.apply_changeset("false").unwrap(), "0".to_owned());
-        assert_eq!(type_option.apply_changeset("no").unwrap(), "0".to_owned());
-        assert_eq!(type_option.apply_changeset("123").unwrap(), "0".to_owned());
+        let data = type_option.apply_changeset("1", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
 
-        assert_eq!(
-            type_option.decode_cell_data("1".to_owned(), &field_meta),
-            "1".to_owned()
-        );
+        let data = type_option.apply_changeset("yes", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), YES);
+
+        let data = type_option.apply_changeset("false", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
+
+        let data = type_option.apply_changeset("no", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
+
+        let data = type_option.apply_changeset("123", None).unwrap();
+        assert_eq!(type_option.decode_cell_data(data, &field_meta), NO);
     }
 }

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

@@ -66,7 +66,7 @@ impl CellDataOperation for DateTypeOption {
     fn apply_changeset<T: Into<CellDataChangeset>>(
         &self,
         changeset: T,
-        cell_meta: Option<CellMeta>,
+        _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
         if let Err(e) = changeset.parse::<i64>() {
@@ -282,7 +282,7 @@ mod tests {
     #[should_panic]
     fn date_description_invalid_data_test() {
         let type_option = DateTypeOption::default();
-        type_option.apply_changeset("he").unwrap();
+        type_option.apply_changeset("he", None).unwrap();
     }
 
     fn data(s: &str) -> String {

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

@@ -104,7 +104,7 @@ impl CellDataOperation for NumberTypeOption {
     fn apply_changeset<T: Into<CellDataChangeset>>(
         &self,
         changeset: T,
-        cell_meta: Option<CellMeta>,
+        _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
         let data = self.strip_symbol(changeset);

+ 94 - 15
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

@@ -5,15 +5,13 @@ use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellD
 use crate::services::util::*;
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{ErrorCode, FlowyError};
 use flowy_grid_data_model::entities::{
     CellMeta, CellMetaChangeset, FieldMeta, FieldType, TypeOptionDataEntity, TypeOptionDataEntry,
 };
-use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
-use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
+use flowy_grid_data_model::parser::NotEmptyUuid;
 use serde::{Deserialize, Serialize};
 use std::str::FromStr;
-use uuid::Uuid;
 
 pub const SELECTION_IDS_SEPARATOR: &str = ",";
 
@@ -66,9 +64,9 @@ impl CellDataOperation for SingleSelectTypeOption {
                 return String::new();
             }
 
-            match select_option_ids(type_option_cell_data.data).pop() {
+            match select_option_ids(type_option_cell_data.data).first() {
                 None => String::new(),
-                Some(option_id) => match self.options.iter().find(|option| option.id == option_id) {
+                Some(option_id) => match self.options.iter().find(|option| &option.id == option_id) {
                     None => String::new(),
                     Some(option) => option.name.clone(),
                 },
@@ -92,7 +90,7 @@ impl CellDataOperation for SingleSelectTypeOption {
             new_cell_data = "".to_string()
         }
 
-        return Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json());
+        Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json())
     }
 }
 
@@ -416,7 +414,7 @@ impl std::default::Default for SelectOptionColor {
     }
 }
 
-fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOption>) -> Vec<SelectOption> {
+fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &[SelectOption]) -> Vec<SelectOption> {
     match cell_meta {
         None => vec![],
         Some(cell_meta) => {
@@ -434,16 +432,97 @@ fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOp
 
 #[cfg(test)]
 mod tests {
-    use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
+    use crate::services::field::FieldBuilder;
+    use crate::services::field::{
+        MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellChangeset,
+        SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
+    };
     use crate::services::row::CellDataOperation;
 
     #[test]
-    #[should_panic]
-    fn selection_description_test() {
-        let type_option = SingleSelectTypeOption::default();
-        assert_eq!(type_option.apply_changeset("1,2,3").unwrap(), "1".to_owned());
+    fn single_select_test() {
+        let google_option = SelectOption::new("Google");
+        let facebook_option = SelectOption::new("Facebook");
+        let twitter_option = SelectOption::new("Twitter");
+        let single_select = SingleSelectTypeOptionBuilder::default()
+            .option(google_option.clone())
+            .option(facebook_option.clone())
+            .option(twitter_option.clone());
+
+        let field_meta = FieldBuilder::new(single_select)
+            .name("Platform")
+            .visibility(true)
+            .build();
+
+        let type_option = SingleSelectTypeOption::from(&field_meta);
+
+        let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
+        let data = SelectOptionCellChangeset::from_insert(&option_ids).cell_data();
+        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
+
+        let data = SelectOptionCellChangeset::from_insert(&google_option.id).cell_data();
+        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
+
+        // Invalid option id
+        let cell_data = type_option
+            .apply_changeset(SelectOptionCellChangeset::from_insert("").cell_data(), None)
+            .unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
+
+        // Invalid option id
+        let cell_data = type_option
+            .apply_changeset(SelectOptionCellChangeset::from_insert("123").cell_data(), None)
+            .unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
+
+        // Invalid changeset
+        assert!(type_option.apply_changeset("123", None).is_err());
+    }
 
-        let type_option = MultiSelectTypeOption::default();
-        assert_eq!(type_option.apply_changeset("1,2,3").unwrap(), "1,2,3".to_owned());
+    #[test]
+    fn multi_select_test() {
+        let google_option = SelectOption::new("Google");
+        let facebook_option = SelectOption::new("Facebook");
+        let twitter_option = SelectOption::new("Twitter");
+        let multi_select = MultiSelectTypeOptionBuilder::default()
+            .option(google_option.clone())
+            .option(facebook_option.clone())
+            .option(twitter_option.clone());
+
+        let field_meta = FieldBuilder::new(multi_select)
+            .name("Platform")
+            .visibility(true)
+            .build();
+
+        let type_option = MultiSelectTypeOption::from(&field_meta);
+
+        let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
+        let data = SelectOptionCellChangeset::from_insert(&option_ids).cell_data();
+        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        assert_eq!(
+            type_option.decode_cell_data(cell_data, &field_meta),
+            vec![google_option.name.clone(), facebook_option.name.clone()].join(SELECTION_IDS_SEPARATOR),
+        );
+
+        let data = SelectOptionCellChangeset::from_insert(&google_option.id).cell_data();
+        let cell_data = type_option.apply_changeset(data, None).unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
+
+        // Invalid option id
+        let cell_data = type_option
+            .apply_changeset(SelectOptionCellChangeset::from_insert("").cell_data(), None)
+            .unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
+
+        // Invalid option id
+        let cell_data = type_option
+            .apply_changeset(SelectOptionCellChangeset::from_insert("123,456").cell_data(), None)
+            .unwrap();
+        assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), "",);
+
+        // Invalid changeset
+        assert!(type_option.apply_changeset("123", None).is_err());
     }
 }

+ 9 - 13
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -50,7 +50,7 @@ impl CellDataOperation for RichTextTypeOption {
     fn apply_changeset<T: Into<CellDataChangeset>>(
         &self,
         changeset: T,
-        cell_meta: Option<CellMeta>,
+        _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let data = changeset.into();
         if data.len() > 10000 {
@@ -85,29 +85,25 @@ mod tests {
         let done_option_id = done_option.id.clone();
         let single_select = SingleSelectTypeOptionBuilder::default().option(done_option);
         let single_select_field_meta = FieldBuilder::new(single_select).build();
-        let data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json();
+        let cell_data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json();
         assert_eq!(
-            type_option.decode_cell_data(data, &single_select_field_meta),
+            type_option.decode_cell_data(cell_data, &single_select_field_meta),
             "Done".to_owned()
         );
 
         // Multiple select
         let google_option = SelectOption::new("Google");
-        let google_option_id = google_option.id.clone();
         let facebook_option = SelectOption::new("Facebook");
-        let face_option_id = facebook_option.id.clone();
+        let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
+        let cell_data_changeset = SelectOptionCellChangeset::from_insert(&ids).cell_data();
         let multi_select = MultiSelectTypeOptionBuilder::default()
             .option(google_option)
-            .option(facebook_option)
-            .option(SelectOption::new("Twitter"));
+            .option(facebook_option);
         let multi_select_field_meta = FieldBuilder::new(multi_select).build();
-        let data = TypeOptionCellData::new(
-            &vec![google_option_id, face_option_id].join(SELECTION_IDS_SEPARATOR),
-            FieldType::SingleSelect,
-        )
-        .json();
+        let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta);
+        let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap();
         assert_eq!(
-            type_option.decode_cell_data(data, &multi_select_field_meta),
+            type_option.decode_cell_data(cell_data, &multi_select_field_meta),
             "Google,Facebook".to_owned()
         );
 

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

@@ -1,6 +1,6 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::manager::GridUser;
-use crate::services::block_meta_editor::GridBlockMetaEditorManager;
+use crate::services::block_meta_manager::GridBlockMetaEditorManager;
 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::row::*;
@@ -154,7 +154,7 @@ impl ClientGridEditor {
 
     pub async fn get_field_meta(&self, field_id: &str) -> Option<FieldMeta> {
         let field_meta = self.pad.read().await.get_field(field_id)?.clone();
-        return Some(field_meta);
+        Some(field_meta)
     }
 
     pub async fn get_field_metas<T>(&self, field_ids: Option<Vec<T>>) -> FlowyResult<Vec<FieldMeta>>
@@ -285,7 +285,7 @@ impl ClientGridEditor {
         match self.pad.read().await.get_field(&changeset.field_id) {
             None => {
                 let msg = format!("Field not found with id: {}", &changeset.field_id);
-                return Err(FlowyError::internal().context(msg));
+                Err(FlowyError::internal().context(msg))
             }
             Some(field_meta) => {
                 // Update the changeset.data property with the return value.

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/mod.rs

@@ -1,6 +1,7 @@
 mod util;
 
 pub mod block_meta_editor;
+mod block_meta_manager;
 pub mod cell;
 pub mod field;
 pub mod grid_editor;

+ 17 - 0
frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs

@@ -1,5 +1,6 @@
 use crate::services::row::apply_cell_data_changeset;
 
+use crate::services::field::SelectOptionCellChangeset;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{CellMeta, FieldMeta, RowMeta, DEFAULT_ROW_HEIGHT};
 use std::collections::HashMap;
@@ -44,6 +45,22 @@ impl<'a> CreateRowMetaBuilder<'a> {
         }
     }
 
+    pub fn add_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
+        match self.field_meta_map.get(&field_id.to_owned()) {
+            None => {
+                let msg = format!("Invalid field_id: {}", field_id);
+                Err(FlowyError::internal().context(msg))
+            }
+            Some(field_meta) => {
+                let cell_data = SelectOptionCellChangeset::from_insert(&data).cell_data();
+                let data = apply_cell_data_changeset(&cell_data, None, field_meta)?;
+                let cell = CellMeta::new(field_id, data);
+                self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
+                Ok(())
+            }
+        }
+    }
+
     #[allow(dead_code)]
     pub fn height(mut self, height: i32) -> Self {
         self.payload.height = height;

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/row/row_entities.rs

@@ -1,6 +1,6 @@
 use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
-use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
+use flowy_grid_data_model::parser::NotEmptyUuid;
 
 #[derive(ProtoBuf, Default)]
 pub struct RowIdentifierPayload {

+ 12 - 1
frontend/rust-lib/flowy-grid/src/util.rs

@@ -8,16 +8,27 @@ pub fn make_default_grid() -> BuildGridContext {
         .visibility(true)
         .build();
 
+    // single select
     let single_select = SingleSelectTypeOptionBuilder::default()
         .option(SelectOption::new("Done"))
         .option(SelectOption::new("Unknown"))
         .option(SelectOption::new("Progress"));
-
     let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
 
+    //multiple select
+    let multi_select = MultiSelectTypeOptionBuilder::default()
+        .option(SelectOption::new("A"))
+        .option(SelectOption::new("B"))
+        .option(SelectOption::new("C"));
+    let multi_select_field = FieldBuilder::new(multi_select)
+        .name("Alphabet")
+        .visibility(true)
+        .build();
+
     GridBuilder::default()
         .add_field(text_field)
         .add_field(single_select_field)
+        .add_field(multi_select_field)
         .add_empty_row()
         .add_empty_row()
         .add_empty_row()

+ 13 - 74
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::{
-    MultiSelectTypeOption, SelectOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
+    MultiSelectTypeOption, SelectOption, SelectOptionCellChangeset, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
 };
 use flowy_grid::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation, CreateRowMetaBuilder};
 use flowy_grid_data_model::entities::{
@@ -233,37 +233,31 @@ async fn grid_row_add_cells_test() {
     for field in &test.field_metas {
         match field.field_type {
             FieldType::RichText => {
-                let data = apply_cell_data_changeset("hello world", field).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                builder.add_cell(&field.id, "hello world".to_owned()).unwrap();
             }
             FieldType::Number => {
-                let data = apply_cell_data_changeset("¥18,443", field).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                builder.add_cell(&field.id, "18,443".to_owned()).unwrap();
             }
             FieldType::DateTime => {
-                let data = apply_cell_data_changeset("1647251762", field).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                builder.add_cell(&field.id, "1647251762".to_owned()).unwrap();
             }
             FieldType::SingleSelect => {
                 let type_option = SingleSelectTypeOption::from(field);
-                let options = type_option.options.first().unwrap();
-                let data = type_option.apply_changeset(&options.id).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                let option = type_option.options.first().unwrap();
+                builder.add_select_option_cell(&field.id, option.id.clone()).unwrap();
             }
             FieldType::MultiSelect => {
                 let type_option = MultiSelectTypeOption::from(field);
-                let options = type_option
+                let ops_ids = type_option
                     .options
                     .iter()
                     .map(|option| option.id.clone())
                     .collect::<Vec<_>>()
                     .join(SELECTION_IDS_SEPARATOR);
-                let data = type_option.apply_changeset(&options).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                builder.add_select_option_cell(&field.id, ops_ids).unwrap();
             }
             FieldType::Checkbox => {
-                let data = apply_cell_data_changeset("false", field).unwrap();
-                builder.add_cell(&field.id, data).unwrap();
+                builder.add_cell(&field.id, "false".to_string()).unwrap();
             }
         }
     }
@@ -272,61 +266,6 @@ async fn grid_row_add_cells_test() {
     test.run_scripts(scripts).await;
 }
 
-#[tokio::test]
-async fn grid_row_add_selection_cell_test() {
-    let mut test = GridEditorTest::new().await;
-    let mut builder = CreateRowMetaBuilder::new(&test.field_metas);
-    let uuid = uuid::Uuid::new_v4().to_string();
-    let mut single_select_field_id = "".to_string();
-    let mut multi_select_field_id = "".to_string();
-    for field in &test.field_metas {
-        match field.field_type {
-            FieldType::SingleSelect => {
-                single_select_field_id = field.id.clone();
-                // The element must be parsed as uuid
-                assert!(builder.add_cell(&field.id, "data".to_owned()).is_err());
-                // // The data should not be empty
-                assert!(builder.add_cell(&field.id, "".to_owned()).is_err());
-                // The element must be parsed as uuid
-                assert!(builder.add_cell(&field.id, "1,2,3".to_owned()).is_err(),);
-                // The separator must be comma
-                assert!(builder.add_cell(&field.id, format!("{}. {}", &uuid, &uuid),).is_err());
-                //
-
-                assert!(builder.add_cell(&field.id, uuid.clone()).is_ok());
-                assert!(builder.add_cell(&field.id, format!("{},   {}", &uuid, &uuid)).is_ok());
-            }
-            FieldType::MultiSelect => {
-                multi_select_field_id = field.id.clone();
-                assert!(builder.add_cell(&field.id, format!("{},   {}", &uuid, &uuid)).is_ok());
-            }
-            _ => {}
-        }
-    }
-    let context = builder.build();
-    assert_eq!(
-        &context
-            .cell_by_field_id
-            .get(&single_select_field_id)
-            .as_ref()
-            .unwrap()
-            .data,
-        &uuid
-    );
-    assert_eq!(
-        context
-            .cell_by_field_id
-            .get(&multi_select_field_id)
-            .as_ref()
-            .unwrap()
-            .data,
-        format!("{},{}", &uuid, &uuid)
-    );
-
-    let scripts = vec![CreateRow { context }];
-    test.run_scripts(scripts).await;
-}
-
 #[tokio::test]
 async fn grid_row_add_date_cell_test() {
     let mut test = GridEditorTest::new().await;
@@ -375,11 +314,11 @@ async fn grid_cell_update() {
                     FieldType::DateTime => "123".to_string(),
                     FieldType::SingleSelect => {
                         let type_option = SingleSelectTypeOption::from(field_meta);
-                        type_option.options.first().unwrap().id.clone()
+                        SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).cell_data()
                     }
                     FieldType::MultiSelect => {
                         let type_option = MultiSelectTypeOption::from(field_meta);
-                        type_option.options.first().unwrap().id.clone()
+                        SelectOptionCellChangeset::from_insert(&type_option.options.first().unwrap().id).cell_data()
                     }
                     FieldType::Checkbox => "1".to_string(),
                 };
@@ -400,8 +339,8 @@ async fn grid_cell_update() {
                     FieldType::RichText => ("1".to_string().repeat(10001), true),
                     FieldType::Number => ("abc".to_string(), true),
                     FieldType::DateTime => ("abc".to_string(), true),
-                    FieldType::SingleSelect => ("abc".to_string(), true),
-                    FieldType::MultiSelect => ("abc".to_string(), true),
+                    FieldType::SingleSelect => (SelectOptionCellChangeset::from_insert("abc").cell_data(), false),
+                    FieldType::MultiSelect => (SelectOptionCellChangeset::from_insert("abc").cell_data(), false),
                     FieldType::Checkbox => ("2".to_string(), false),
                 };
 

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

@@ -3,8 +3,8 @@ use flowy_grid::services::field::*;
 use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
 use flowy_grid::services::row::CreateRowMetaPayload;
 use flowy_grid_data_model::entities::{
-    BuildGridContext, CellMetaChangeset, CreateFieldParams, Field, FieldChangesetParams, FieldMeta, FieldType,
-    GridBlockMeta, GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder, TypeOptionDataEntry,
+    BuildGridContext, CellMetaChangeset, CreateFieldParams, Field, FieldChangesetParams, FieldMeta, FieldOrder,
+    FieldType, GridBlockMeta, GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder, TypeOptionDataEntry,
 };
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_sync::client_grid::GridBuilder;
@@ -89,7 +89,7 @@ impl GridEditorTest {
         let view_data: Bytes = build_context.try_into().unwrap();
         let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
-        let field_metas = editor.get_field_metas(None).await.unwrap();
+        let field_metas = editor.get_field_metas::<FieldOrder>(None).await.unwrap();
         let grid_blocks = editor.get_block_metas().await.unwrap();
         let row_metas = get_row_metas(&editor).await;
 
@@ -125,12 +125,12 @@ impl GridEditorTest {
                 }
 
                 self.editor.create_field(params).await.unwrap();
-                self.field_metas = self.editor.get_field_metas(None).await.unwrap();
+                self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
                 assert_eq!(self.field_count, self.field_metas.len());
             }
             EditorScript::UpdateField { changeset: change } => {
                 self.editor.update_field(change).await.unwrap();
-                self.field_metas = self.editor.get_field_metas(None).await.unwrap();
+                self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
             }
             EditorScript::DeleteField { field_meta } => {
                 if self.editor.contain_field(&field_meta.id).await {
@@ -138,17 +138,20 @@ impl GridEditorTest {
                 }
 
                 self.editor.delete_field(&field_meta.id).await.unwrap();
-                self.field_metas = self.editor.get_field_metas(None).await.unwrap();
+                self.field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
                 assert_eq!(self.field_count, self.field_metas.len());
             }
             EditorScript::AssertFieldCount(count) => {
-                assert_eq!(self.editor.get_field_metas(None).await.unwrap().len(), count);
+                assert_eq!(
+                    self.editor.get_field_metas::<FieldOrder>(None).await.unwrap().len(),
+                    count
+                );
             }
             EditorScript::AssertFieldEqual {
                 field_index,
                 field_meta,
             } => {
-                let field_metas = self.editor.get_field_metas(None).await.unwrap();
+                let field_metas = self.editor.get_field_metas::<FieldOrder>(None).await.unwrap();
                 assert_eq!(field_metas[field_index].clone(), field_meta);
             }
             EditorScript::CreateBlock { block } => {

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

@@ -1,5 +1,5 @@
 use crate::entities::{FieldMeta, FieldType, RowMeta};
-use crate::parser::{NotEmptyStr, NotEmptyUuid};
+use crate::parser::NotEmptyUuid;
 use flowy_derive::ProtoBuf;
 use flowy_error_code::ErrorCode;
 use std::collections::HashMap;
@@ -422,6 +422,7 @@ pub struct CreateFieldPayload {
     pub start_field_id: Option<String>,
 }
 
+#[derive(Clone)]
 pub struct CreateFieldParams {
     pub grid_id: String,
     pub field: Field,

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

@@ -396,7 +396,7 @@ impl std::convert::From<CellMetaChangeset> for RowMetaChangeset {
         let field_id = changeset.field_id;
         let cell_meta = CellMeta {
             field_id: field_id.clone(),
-            data: changeset.data.unwrap_or("".to_owned()),
+            data: changeset.data.unwrap_or_else(|| "".to_owned()),
         };
         cell_by_field_id.insert(field_id, cell_meta);
 

+ 0 - 3
shared-lib/flowy-grid-data-model/src/protobuf/model/mod.rs

@@ -4,8 +4,5 @@
 mod grid;
 pub use grid::*;
 
-mod type_option;
-pub use type_option::*;
-
 mod meta;
 pub use meta::*;

+ 0 - 978
shared-lib/flowy-grid-data-model/src/protobuf/model/type_option.rs

@@ -1,978 +0,0 @@
-// This file is generated by rust-protobuf 2.25.2. Do not edit
-// @generated
-
-// https://github.com/rust-lang/rust-clippy/issues/702
-#![allow(unknown_lints)]
-#![allow(clippy::all)]
-
-#![allow(unused_attributes)]
-#![cfg_attr(rustfmt, rustfmt::skip)]
-
-#![allow(box_pointers)]
-#![allow(dead_code)]
-#![allow(missing_docs)]
-#![allow(non_camel_case_types)]
-#![allow(non_snake_case)]
-#![allow(non_upper_case_globals)]
-#![allow(trivial_casts)]
-#![allow(unused_imports)]
-#![allow(unused_results)]
-//! Generated file from `type_option.proto`
-
-/// Generated files are compatible only with the same version
-/// of protobuf runtime.
-// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
-
-#[derive(PartialEq,Clone,Default)]
-pub struct SelectOption {
-    // message fields
-    pub id: ::std::string::String,
-    pub name: ::std::string::String,
-    pub color: SelectOptionColor,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a SelectOption {
-    fn default() -> &'a SelectOption {
-        <SelectOption as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl SelectOption {
-    pub fn new() -> SelectOption {
-        ::std::default::Default::default()
-    }
-
-    // string id = 1;
-
-
-    pub fn get_id(&self) -> &str {
-        &self.id
-    }
-    pub fn clear_id(&mut self) {
-        self.id.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_id(&mut self, v: ::std::string::String) {
-        self.id = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_id(&mut self) -> &mut ::std::string::String {
-        &mut self.id
-    }
-
-    // Take field
-    pub fn take_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.id, ::std::string::String::new())
-    }
-
-    // string name = 2;
-
-
-    pub fn get_name(&self) -> &str {
-        &self.name
-    }
-    pub fn clear_name(&mut self) {
-        self.name.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_name(&mut self, v: ::std::string::String) {
-        self.name = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_name(&mut self) -> &mut ::std::string::String {
-        &mut self.name
-    }
-
-    // Take field
-    pub fn take_name(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.name, ::std::string::String::new())
-    }
-
-    // .SelectOptionColor color = 3;
-
-
-    pub fn get_color(&self) -> SelectOptionColor {
-        self.color
-    }
-    pub fn clear_color(&mut self) {
-        self.color = SelectOptionColor::Purple;
-    }
-
-    // Param is passed by value, moved
-    pub fn set_color(&mut self, v: SelectOptionColor) {
-        self.color = v;
-    }
-}
-
-impl ::protobuf::Message for SelectOption {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
-                },
-                2 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
-                },
-                3 => {
-                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.color, 3, &mut self.unknown_fields)?
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        if !self.id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.id);
-        }
-        if !self.name.is_empty() {
-            my_size += ::protobuf::rt::string_size(2, &self.name);
-        }
-        if self.color != SelectOptionColor::Purple {
-            my_size += ::protobuf::rt::enum_size(3, self.color);
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.id.is_empty() {
-            os.write_string(1, &self.id)?;
-        }
-        if !self.name.is_empty() {
-            os.write_string(2, &self.name)?;
-        }
-        if self.color != SelectOptionColor::Purple {
-            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.color))?;
-        }
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> SelectOption {
-        SelectOption::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "id",
-                |m: &SelectOption| { &m.id },
-                |m: &mut SelectOption| { &mut m.id },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "name",
-                |m: &SelectOption| { &m.name },
-                |m: &mut SelectOption| { &mut m.name },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<SelectOptionColor>>(
-                "color",
-                |m: &SelectOption| { &m.color },
-                |m: &mut SelectOption| { &mut m.color },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOption>(
-                "SelectOption",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static SelectOption {
-        static instance: ::protobuf::rt::LazyV2<SelectOption> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SelectOption::new)
-    }
-}
-
-impl ::protobuf::Clear for SelectOption {
-    fn clear(&mut self) {
-        self.id.clear();
-        self.name.clear();
-        self.color = SelectOptionColor::Purple;
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for SelectOption {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for SelectOption {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
-#[derive(PartialEq,Clone,Default)]
-pub struct SelectOptionCellChangesetPayload {
-    // message fields
-    pub grid_id: ::std::string::String,
-    pub row_id: ::std::string::String,
-    pub field_id: ::std::string::String,
-    // message oneof groups
-    pub one_of_insert_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id>,
-    pub one_of_delete_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id>,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a SelectOptionCellChangesetPayload {
-    fn default() -> &'a SelectOptionCellChangesetPayload {
-        <SelectOptionCellChangesetPayload as ::protobuf::Message>::default_instance()
-    }
-}
-
-#[derive(Clone,PartialEq,Debug)]
-pub enum SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id {
-    insert_option_id(::std::string::String),
-}
-
-#[derive(Clone,PartialEq,Debug)]
-pub enum SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id {
-    delete_option_id(::std::string::String),
-}
-
-impl SelectOptionCellChangesetPayload {
-    pub fn new() -> SelectOptionCellChangesetPayload {
-        ::std::default::Default::default()
-    }
-
-    // string grid_id = 1;
-
-
-    pub fn get_grid_id(&self) -> &str {
-        &self.grid_id
-    }
-    pub fn clear_grid_id(&mut self) {
-        self.grid_id.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_grid_id(&mut self, v: ::std::string::String) {
-        self.grid_id = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
-        &mut self.grid_id
-    }
-
-    // Take field
-    pub fn take_grid_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
-    }
-
-    // string row_id = 2;
-
-
-    pub fn get_row_id(&self) -> &str {
-        &self.row_id
-    }
-    pub fn clear_row_id(&mut self) {
-        self.row_id.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_row_id(&mut self, v: ::std::string::String) {
-        self.row_id = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
-        &mut self.row_id
-    }
-
-    // Take field
-    pub fn take_row_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.row_id, ::std::string::String::new())
-    }
-
-    // string field_id = 3;
-
-
-    pub fn get_field_id(&self) -> &str {
-        &self.field_id
-    }
-    pub fn clear_field_id(&mut self) {
-        self.field_id.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_field_id(&mut self, v: ::std::string::String) {
-        self.field_id = v;
-    }
-
-    // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
-        &mut self.field_id
-    }
-
-    // Take field
-    pub fn take_field_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
-    }
-
-    // string insert_option_id = 4;
-
-
-    pub fn get_insert_option_id(&self) -> &str {
-        match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v)) => v,
-            _ => "",
-        }
-    }
-    pub fn clear_insert_option_id(&mut self) {
-        self.one_of_insert_option_id = ::std::option::Option::None;
-    }
-
-    pub fn has_insert_option_id(&self) -> bool {
-        match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(..)) => true,
-            _ => false,
-        }
-    }
-
-    // Param is passed by value, moved
-    pub fn set_insert_option_id(&mut self, v: ::std::string::String) {
-        self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v))
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_insert_option_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(_)) = self.one_of_insert_option_id {
-        } else {
-            self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(::std::string::String::new()));
-        }
-        match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref mut v)) => v,
-            _ => panic!(),
-        }
-    }
-
-    // Take field
-    pub fn take_insert_option_id(&mut self) -> ::std::string::String {
-        if self.has_insert_option_id() {
-            match self.one_of_insert_option_id.take() {
-                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v)) => v,
-                _ => panic!(),
-            }
-        } else {
-            ::std::string::String::new()
-        }
-    }
-
-    // string delete_option_id = 5;
-
-
-    pub fn get_delete_option_id(&self) -> &str {
-        match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v)) => v,
-            _ => "",
-        }
-    }
-    pub fn clear_delete_option_id(&mut self) {
-        self.one_of_delete_option_id = ::std::option::Option::None;
-    }
-
-    pub fn has_delete_option_id(&self) -> bool {
-        match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(..)) => true,
-            _ => false,
-        }
-    }
-
-    // Param is passed by value, moved
-    pub fn set_delete_option_id(&mut self, v: ::std::string::String) {
-        self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v))
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_delete_option_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(_)) = self.one_of_delete_option_id {
-        } else {
-            self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(::std::string::String::new()));
-        }
-        match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref mut v)) => v,
-            _ => panic!(),
-        }
-    }
-
-    // Take field
-    pub fn take_delete_option_id(&mut self) -> ::std::string::String {
-        if self.has_delete_option_id() {
-            match self.one_of_delete_option_id.take() {
-                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v)) => v,
-                _ => panic!(),
-            }
-        } else {
-            ::std::string::String::new()
-        }
-    }
-}
-
-impl ::protobuf::Message for SelectOptionCellChangesetPayload {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
-                },
-                2 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
-                },
-                3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
-                },
-                4 => {
-                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
-                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
-                    }
-                    self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(is.read_string()?));
-                },
-                5 => {
-                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
-                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
-                    }
-                    self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(is.read_string()?));
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        if !self.grid_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
-        }
-        if !self.row_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(2, &self.row_id);
-        }
-        if !self.field_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.field_id);
-        }
-        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
-            match v {
-                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
-                    my_size += ::protobuf::rt::string_size(4, &v);
-                },
-            };
-        }
-        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
-            match v {
-                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
-                    my_size += ::protobuf::rt::string_size(5, &v);
-                },
-            };
-        }
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.grid_id.is_empty() {
-            os.write_string(1, &self.grid_id)?;
-        }
-        if !self.row_id.is_empty() {
-            os.write_string(2, &self.row_id)?;
-        }
-        if !self.field_id.is_empty() {
-            os.write_string(3, &self.field_id)?;
-        }
-        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
-            match v {
-                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
-                    os.write_string(4, v)?;
-                },
-            };
-        }
-        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
-            match v {
-                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
-                    os.write_string(5, v)?;
-                },
-            };
-        }
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> SelectOptionCellChangesetPayload {
-        SelectOptionCellChangesetPayload::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "grid_id",
-                |m: &SelectOptionCellChangesetPayload| { &m.grid_id },
-                |m: &mut SelectOptionCellChangesetPayload| { &mut m.grid_id },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "row_id",
-                |m: &SelectOptionCellChangesetPayload| { &m.row_id },
-                |m: &mut SelectOptionCellChangesetPayload| { &mut m.row_id },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "field_id",
-                |m: &SelectOptionCellChangesetPayload| { &m.field_id },
-                |m: &mut SelectOptionCellChangesetPayload| { &mut m.field_id },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
-                "insert_option_id",
-                SelectOptionCellChangesetPayload::has_insert_option_id,
-                SelectOptionCellChangesetPayload::get_insert_option_id,
-            ));
-            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
-                "delete_option_id",
-                SelectOptionCellChangesetPayload::has_delete_option_id,
-                SelectOptionCellChangesetPayload::get_delete_option_id,
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionCellChangesetPayload>(
-                "SelectOptionCellChangesetPayload",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static SelectOptionCellChangesetPayload {
-        static instance: ::protobuf::rt::LazyV2<SelectOptionCellChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SelectOptionCellChangesetPayload::new)
-    }
-}
-
-impl ::protobuf::Clear for SelectOptionCellChangesetPayload {
-    fn clear(&mut self) {
-        self.grid_id.clear();
-        self.row_id.clear();
-        self.field_id.clear();
-        self.one_of_insert_option_id = ::std::option::Option::None;
-        self.one_of_delete_option_id = ::std::option::Option::None;
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for SelectOptionCellChangesetPayload {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for SelectOptionCellChangesetPayload {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
-#[derive(PartialEq,Clone,Default)]
-pub struct SelectOptionContext {
-    // message fields
-    pub options: ::protobuf::RepeatedField<SelectOption>,
-    pub select_options: ::protobuf::RepeatedField<SelectOption>,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a SelectOptionContext {
-    fn default() -> &'a SelectOptionContext {
-        <SelectOptionContext as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl SelectOptionContext {
-    pub fn new() -> SelectOptionContext {
-        ::std::default::Default::default()
-    }
-
-    // repeated .SelectOption options = 1;
-
-
-    pub fn get_options(&self) -> &[SelectOption] {
-        &self.options
-    }
-    pub fn clear_options(&mut self) {
-        self.options.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
-        self.options = v;
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
-        &mut self.options
-    }
-
-    // Take field
-    pub fn take_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
-        ::std::mem::replace(&mut self.options, ::protobuf::RepeatedField::new())
-    }
-
-    // repeated .SelectOption select_options = 2;
-
-
-    pub fn get_select_options(&self) -> &[SelectOption] {
-        &self.select_options
-    }
-    pub fn clear_select_options(&mut self) {
-        self.select_options.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_select_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
-        self.select_options = v;
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_select_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
-        &mut self.select_options
-    }
-
-    // Take field
-    pub fn take_select_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
-        ::std::mem::replace(&mut self.select_options, ::protobuf::RepeatedField::new())
-    }
-}
-
-impl ::protobuf::Message for SelectOptionContext {
-    fn is_initialized(&self) -> bool {
-        for v in &self.options {
-            if !v.is_initialized() {
-                return false;
-            }
-        };
-        for v in &self.select_options {
-            if !v.is_initialized() {
-                return false;
-            }
-        };
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.options)?;
-                },
-                2 => {
-                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.select_options)?;
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        for value in &self.options {
-            let len = value.compute_size();
-            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
-        };
-        for value in &self.select_options {
-            let len = value.compute_size();
-            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
-        };
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        for v in &self.options {
-            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
-            os.write_raw_varint32(v.get_cached_size())?;
-            v.write_to_with_cached_sizes(os)?;
-        };
-        for v in &self.select_options {
-            os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
-            os.write_raw_varint32(v.get_cached_size())?;
-            v.write_to_with_cached_sizes(os)?;
-        };
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> SelectOptionContext {
-        SelectOptionContext::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
-                "options",
-                |m: &SelectOptionContext| { &m.options },
-                |m: &mut SelectOptionContext| { &mut m.options },
-            ));
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
-                "select_options",
-                |m: &SelectOptionContext| { &m.select_options },
-                |m: &mut SelectOptionContext| { &mut m.select_options },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionContext>(
-                "SelectOptionContext",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static SelectOptionContext {
-        static instance: ::protobuf::rt::LazyV2<SelectOptionContext> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SelectOptionContext::new)
-    }
-}
-
-impl ::protobuf::Clear for SelectOptionContext {
-    fn clear(&mut self) {
-        self.options.clear();
-        self.select_options.clear();
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for SelectOptionContext {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for SelectOptionContext {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
-#[derive(Clone,PartialEq,Eq,Debug,Hash)]
-pub enum SelectOptionColor {
-    Purple = 0,
-    Pink = 1,
-    LightPink = 2,
-    Orange = 3,
-    Yellow = 4,
-    Lime = 5,
-    Green = 6,
-    Aqua = 7,
-    Blue = 8,
-}
-
-impl ::protobuf::ProtobufEnum for SelectOptionColor {
-    fn value(&self) -> i32 {
-        *self as i32
-    }
-
-    fn from_i32(value: i32) -> ::std::option::Option<SelectOptionColor> {
-        match value {
-            0 => ::std::option::Option::Some(SelectOptionColor::Purple),
-            1 => ::std::option::Option::Some(SelectOptionColor::Pink),
-            2 => ::std::option::Option::Some(SelectOptionColor::LightPink),
-            3 => ::std::option::Option::Some(SelectOptionColor::Orange),
-            4 => ::std::option::Option::Some(SelectOptionColor::Yellow),
-            5 => ::std::option::Option::Some(SelectOptionColor::Lime),
-            6 => ::std::option::Option::Some(SelectOptionColor::Green),
-            7 => ::std::option::Option::Some(SelectOptionColor::Aqua),
-            8 => ::std::option::Option::Some(SelectOptionColor::Blue),
-            _ => ::std::option::Option::None
-        }
-    }
-
-    fn values() -> &'static [Self] {
-        static values: &'static [SelectOptionColor] = &[
-            SelectOptionColor::Purple,
-            SelectOptionColor::Pink,
-            SelectOptionColor::LightPink,
-            SelectOptionColor::Orange,
-            SelectOptionColor::Yellow,
-            SelectOptionColor::Lime,
-            SelectOptionColor::Green,
-            SelectOptionColor::Aqua,
-            SelectOptionColor::Blue,
-        ];
-        values
-    }
-
-    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            ::protobuf::reflect::EnumDescriptor::new_pb_name::<SelectOptionColor>("SelectOptionColor", file_descriptor_proto())
-        })
-    }
-}
-
-impl ::std::marker::Copy for SelectOptionColor {
-}
-
-impl ::std::default::Default for SelectOptionColor {
-    fn default() -> Self {
-        SelectOptionColor::Purple
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for SelectOptionColor {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
-    }
-}
-
-static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x11type_option.proto\"\\\n\x0cSelectOption\x12\x0e\n\x02id\x18\x01\
-    \x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12(\n\
-    \x05color\x18\x03\x20\x01(\x0e2\x12.SelectOptionColorR\x05color\"\xfb\
-    \x01\n\x20SelectOptionCellChangesetPayload\x12\x17\n\x07grid_id\x18\x01\
-    \x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\
-    \x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12*\n\x10insert_o\
-    ption_id\x18\x04\x20\x01(\tH\0R\x0einsertOptionId\x12*\n\x10delete_optio\
-    n_id\x18\x05\x20\x01(\tH\x01R\x0edeleteOptionIdB\x19\n\x17one_of_insert_\
-    option_idB\x19\n\x17one_of_delete_option_id\"t\n\x13SelectOptionContext\
-    \x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x124\
-    \n\x0eselect_options\x18\x02\x20\x03(\x0b2\r.SelectOptionR\rselectOption\
-    s*y\n\x11SelectOptionColor\x12\n\n\x06Purple\x10\0\x12\x08\n\x04Pink\x10\
-    \x01\x12\r\n\tLightPink\x10\x02\x12\n\n\x06Orange\x10\x03\x12\n\n\x06Yel\
-    low\x10\x04\x12\x08\n\x04Lime\x10\x05\x12\t\n\x05Green\x10\x06\x12\x08\n\
-    \x04Aqua\x10\x07\x12\x08\n\x04Blue\x10\x08b\x06proto3\
-";
-
-static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
-
-fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
-    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
-}
-
-pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
-    file_descriptor_proto_lazy.get(|| {
-        parse_descriptor_proto()
-    })
-}

+ 0 - 29
shared-lib/flowy-grid-data-model/src/protobuf/proto/type_option.proto

@@ -1,29 +0,0 @@
-syntax = "proto3";
-
-message SelectOption {
-    string id = 1;
-    string name = 2;
-    SelectOptionColor color = 3;
-}
-message SelectOptionCellChangesetPayload {
-    string grid_id = 1;
-    string row_id = 2;
-    string field_id = 3;
-    oneof one_of_insert_option_id { string insert_option_id = 4; };
-    oneof one_of_delete_option_id { string delete_option_id = 5; };
-}
-message SelectOptionContext {
-    repeated SelectOption options = 1;
-    repeated SelectOption select_options = 2;
-}
-enum SelectOptionColor {
-    Purple = 0;
-    Pink = 1;
-    LightPink = 2;
-    Orange = 3;
-    Yellow = 4;
-    Lime = 5;
-    Green = 6;
-    Aqua = 7;
-    Blue = 8;
-}