Kaynağa Gözat

chore: impl handler logic

appflowy 3 yıl önce
ebeveyn
işleme
8029f1035d
30 değiştirilmiş dosya ile 303 ekleme ve 177 silme
  1. 2 2
      frontend/app_flowy/lib/plugin/plugin.dart
  2. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart
  3. 0 17
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  4. 2 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbenum.dart
  5. 2 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart
  6. 4 6
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  7. 5 6
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  8. 1 0
      frontend/rust-lib/Cargo.lock
  9. 3 3
      frontend/rust-lib/flowy-block/src/manager.rs
  10. 3 6
      frontend/rust-lib/flowy-folder/src/controller.rs
  11. 4 0
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs
  12. 10 16
      frontend/rust-lib/flowy-folder/src/services/view/controller.rs
  13. 2 2
      frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs
  14. 1 1
      frontend/rust-lib/flowy-grid/Cargo.toml
  15. 19 25
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  16. 4 8
      frontend/rust-lib/flowy-grid/src/event_map.rs
  17. 89 10
      frontend/rust-lib/flowy-grid/src/manager.rs
  18. 12 15
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  19. 4 5
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  20. 40 31
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  21. 1 1
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  22. 2 2
      frontend/rust-lib/flowy-net/src/local_server/server.rs
  23. 3 3
      shared-lib/flowy-collaboration/src/client_document/default/mod.rs
  24. 2 2
      shared-lib/flowy-collaboration/src/client_document/document_pad.rs
  25. 16 4
      shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs
  26. 6 6
      shared-lib/flowy-collaboration/src/entities/revision.rs
  27. 2 0
      shared-lib/flowy-folder-data-model/src/entities/view.rs
  28. 6 3
      shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs
  29. 1 0
      shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto
  30. 56 1
      shared-lib/flowy-grid-data-model/src/entities/grid.rs

+ 2 - 2
frontend/app_flowy/lib/plugin/plugin.dart

@@ -10,7 +10,7 @@ import 'package:flutter/widgets.dart';
 export "./src/sandbox.dart";
 
 enum DefaultPlugin {
-  quillEditor,
+  quill,
   blank,
   trash,
   grid,
@@ -19,7 +19,7 @@ enum DefaultPlugin {
 extension FlowyDefaultPluginExt on DefaultPlugin {
   int type() {
     switch (this) {
-      case DefaultPlugin.quillEditor:
+      case DefaultPlugin.quill:
         return 0;
       case DefaultPlugin.blank:
         return 1;

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart

@@ -47,7 +47,7 @@ class DocumentPluginBuilder extends PluginBuilder {
   String get menuName => "Doc";
 
   @override
-  PluginType get pluginType => DefaultPlugin.quillEditor.type();
+  PluginType get pluginType => DefaultPlugin.quill.type();
 
   @override
   ViewDataType get dataType => ViewDataType.RichText;

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

@@ -1,23 +1,6 @@
 
 /// Auto generate. Do not edit
 part of '../../dispatch.dart';
-class GridEventCreateGrid {
-     CreateGridPayload request;
-     GridEventCreateGrid(this.request);
-
-    Future<Either<Grid, FlowyError>> send() {
-    final request = FFIRequest.create()
-          ..event = GridEvent.CreateGrid.toString()
-          ..payload = requestToBytes(this.request);
-
-    return Dispatch.asyncRequest(request)
-        .then((bytesResult) => bytesResult.fold(
-           (okBytes) => left(Grid.fromBuffer(okBytes)),
-           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
-        ));
-    }
-}
-
 class GridEventOpenGrid {
      GridId request;
      GridEventOpenGrid(this.request);

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

@@ -12,10 +12,12 @@ import 'package:protobuf/protobuf.dart' as $pb;
 class ViewDataType extends $pb.ProtobufEnum {
   static const ViewDataType RichText = ViewDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
   static const ViewDataType PlainText = ViewDataType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PlainText');
+  static const ViewDataType Grid = ViewDataType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Grid');
 
   static const $core.List<ViewDataType> values = <ViewDataType> [
     RichText,
     PlainText,
+    Grid,
   ];
 
   static final $core.Map<$core.int, ViewDataType> _byValue = $pb.ProtobufEnum.initByValue(values);

+ 2 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart

@@ -14,11 +14,12 @@ const ViewDataType$json = const {
   '2': const [
     const {'1': 'RichText', '2': 0},
     const {'1': 'PlainText', '2': 1},
+    const {'1': 'Grid', '2': 2},
   ],
 };
 
 /// Descriptor for `ViewDataType`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List viewDataTypeDescriptor = $convert.base64Decode('CgxWaWV3RGF0YVR5cGUSDAoIUmljaFRleHQQABINCglQbGFpblRleHQQAQ==');
+final $typed_data.Uint8List viewDataTypeDescriptor = $convert.base64Decode('CgxWaWV3RGF0YVR5cGUSDAoIUmljaFRleHQQABINCglQbGFpblRleHQQARIICgRHcmlkEAI=');
 @$core.Deprecated('Use viewDescriptor instead')
 const View$json = const {
   '1': 'View',

+ 4 - 6
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart

@@ -10,14 +10,12 @@ import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
 class GridEvent extends $pb.ProtobufEnum {
-  static const GridEvent CreateGrid = GridEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateGrid');
-  static const GridEvent OpenGrid = GridEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenGrid');
-  static const GridEvent GetRows = GridEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRows');
-  static const GridEvent GetFields = GridEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFields');
-  static const GridEvent CreateRow = GridEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
+  static const GridEvent OpenGrid = GridEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenGrid');
+  static const GridEvent GetRows = GridEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRows');
+  static const GridEvent GetFields = GridEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFields');
+  static const GridEvent CreateRow = GridEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
 
   static const $core.List<GridEvent> values = <GridEvent> [
-    CreateGrid,
     OpenGrid,
     GetRows,
     GetFields,

+ 5 - 6
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart

@@ -12,13 +12,12 @@ import 'dart:typed_data' as $typed_data;
 const GridEvent$json = const {
   '1': 'GridEvent',
   '2': const [
-    const {'1': 'CreateGrid', '2': 0},
-    const {'1': 'OpenGrid', '2': 1},
-    const {'1': 'GetRows', '2': 2},
-    const {'1': 'GetFields', '2': 3},
-    const {'1': 'CreateRow', '2': 4},
+    const {'1': 'OpenGrid', '2': 0},
+    const {'1': 'GetRows', '2': 1},
+    const {'1': 'GetFields', '2': 2},
+    const {'1': 'CreateRow', '2': 3},
   ],
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDgoKQ3JlYXRlR3JpZBAAEgwKCE9wZW5HcmlkEAESCwoHR2V0Um93cxACEg0KCUdldEZpZWxkcxADEg0KCUNyZWF0ZVJvdxAE');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDAoIT3BlbkdyaWQQABILCgdHZXRSb3dzEAESDQoJR2V0RmllbGRzEAISDQoJQ3JlYXRlUm93EAM=');

+ 1 - 0
frontend/rust-lib/Cargo.lock

@@ -1071,6 +1071,7 @@ dependencies = [
  "rusty-money",
  "strum",
  "strum_macros",
+ "tokio",
  "tracing",
  "uuid",
 ]

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

@@ -85,7 +85,7 @@ impl BlockManager {
         let doc_id = doc_id.as_ref().to_owned();
         let db_pool = self.block_user.db_pool()?;
         // Maybe we could save the block to disk without creating the RevisionManager
-        let rev_manager = self.make_rev_manager(&doc_id, db_pool)?;
+        let rev_manager = self.make_block_rev_manager(&doc_id, db_pool)?;
         let _ = rev_manager.reset_object(revisions).await?;
         Ok(())
     }
@@ -125,7 +125,7 @@ impl BlockManager {
     ) -> Result<Arc<ClientBlockEditor>, FlowyError> {
         let user = self.block_user.clone();
         let token = self.block_user.token()?;
-        let rev_manager = self.make_rev_manager(block_id, pool.clone())?;
+        let rev_manager = self.make_block_rev_manager(block_id, pool.clone())?;
         let cloud_service = Arc::new(BlockRevisionCloudService {
             token,
             server: self.cloud_service.clone(),
@@ -136,7 +136,7 @@ impl BlockManager {
         Ok(doc_editor)
     }
 
-    fn make_rev_manager(&self, doc_id: &str, pool: Arc<ConnectionPool>) -> Result<RevisionManager, FlowyError> {
+    fn make_block_rev_manager(&self, doc_id: &str, pool: Arc<ConnectionPool>) -> Result<RevisionManager, FlowyError> {
         let user_id = self.block_user.user_id()?;
         let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, doc_id, pool));
         Ok(RevisionManager::new(&user_id, doc_id, rev_persistence))

+ 3 - 6
frontend/rust-lib/flowy-folder/src/controller.rs

@@ -1,6 +1,6 @@
 use bytes::Bytes;
 use chrono::Utc;
-use flowy_collaboration::client_document::default::{initial_delta, initial_read_me};
+use flowy_collaboration::client_document::default::{initial_quill_delta, initial_quill_delta_string, initial_read_me};
 use flowy_folder_data_model::user_default;
 use flowy_sync::RevisionWebSocket;
 use lazy_static::lazy_static;
@@ -199,13 +199,10 @@ impl DefaultFolderBuilder {
                 let view_data = if index == 0 {
                     initial_read_me().to_delta_json()
                 } else {
-                    initial_delta().to_delta_json()
+                    initial_quill_delta_string()
                 };
                 view_controller.set_latest_view(view);
-                let delta_data = Bytes::from(view_data);
-                let repeated_revision: RepeatedRevision =
-                    Revision::initial_revision(user_id, &view.id, delta_data).into();
-                let _ = view_controller.create_view(&view.id, repeated_revision).await?;
+                let _ = view_controller.create_view(&view.id, Bytes::from(view_data)).await?;
             }
         }
         let folder = FolderPad::new(vec![workspace.clone()], vec![])?;

+ 4 - 0
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs

@@ -86,6 +86,7 @@ impl ViewTable {
         let data_type = match view.data_type {
             ViewDataType::RichText => SqlViewDataType::RichText,
             ViewDataType::PlainText => SqlViewDataType::PlainText,
+            ViewDataType::Grid => SqlViewDataType::Grid,
         };
 
         ViewTable {
@@ -108,6 +109,7 @@ impl std::convert::From<ViewTable> for View {
         let data_type = match table.view_type {
             SqlViewDataType::RichText => ViewDataType::RichText,
             SqlViewDataType::PlainText => ViewDataType::PlainText,
+            SqlViewDataType::Grid => ViewDataType::Grid,
         };
 
         View {
@@ -179,6 +181,7 @@ impl ViewChangeset {
 pub enum SqlViewDataType {
     RichText = 0,
     PlainText = 1,
+    Grid = 2,
 }
 
 impl std::default::Default for SqlViewDataType {
@@ -192,6 +195,7 @@ impl std::convert::From<i32> for SqlViewDataType {
         match value {
             0 => SqlViewDataType::RichText,
             1 => SqlViewDataType::PlainText,
+            2 => SqlViewDataType::Grid,
             o => {
                 log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
                 SqlViewDataType::PlainText

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

@@ -4,7 +4,7 @@ use flowy_collaboration::entities::{
     revision::{RepeatedRevision, Revision},
 };
 
-use flowy_collaboration::client_document::default::initial_delta_string;
+use flowy_collaboration::client_document::default::initial_quill_delta_string;
 use futures::{FutureExt, StreamExt};
 use std::{collections::HashSet, sync::Arc};
 
@@ -63,30 +63,24 @@ impl ViewController {
     #[tracing::instrument(level = "trace", skip(self, params), fields(name = %params.name), err)]
     pub(crate) async fn create_view_from_params(&self, params: CreateViewParams) -> Result<View, FlowyError> {
         let view_data = if params.data.is_empty() {
-            initial_delta_string()
+            initial_quill_delta_string()
         } else {
             params.data.clone()
         };
 
-        let delta_data = Bytes::from(view_data);
-        let user_id = self.user.user_id()?;
-        let repeated_revision: RepeatedRevision =
-            Revision::initial_revision(&user_id, &params.view_id, delta_data).into();
-        let _ = self.create_view(&params.view_id, repeated_revision).await?;
+        let _ = self.create_view(&params.view_id, Bytes::from(view_data)).await?;
         let view = self.create_view_on_server(params).await?;
         let _ = self.create_view_on_local(view.clone()).await?;
         Ok(view)
     }
 
-    #[tracing::instrument(level = "debug", skip(self, view_id, repeated_revision), err)]
-    pub(crate) async fn create_view(
-        &self,
-        view_id: &str,
-        repeated_revision: RepeatedRevision,
-    ) -> Result<(), FlowyError> {
-        if repeated_revision.is_empty() {
+    #[tracing::instrument(level = "debug", skip(self, view_id, delta_data), err)]
+    pub(crate) async fn create_view(&self, view_id: &str, delta_data: Bytes) -> Result<(), FlowyError> {
+        if delta_data.is_empty() {
             return Err(FlowyError::internal().context("The content of the view should not be empty"));
         }
+        let user_id = self.user.user_id()?;
+        let repeated_revision: RepeatedRevision = Revision::initial_revision(&user_id, view_id, delta_data).into();
         let _ = self.block_manager.create_block(view_id, repeated_revision).await?;
         Ok(())
     }
@@ -304,7 +298,7 @@ impl ViewController {
     fn listen_trash_can_event(&self) {
         let mut rx = self.trash_controller.subscribe();
         let persistence = self.persistence.clone();
-        let document_manager = self.block_manager.clone();
+        let block_manager = self.block_manager.clone();
         let trash_controller = self.trash_controller.clone();
         let _ = tokio::spawn(async move {
             loop {
@@ -318,7 +312,7 @@ impl ViewController {
                 if let Some(event) = stream.next().await {
                     handle_trash_event(
                         persistence.clone(),
-                        document_manager.clone(),
+                        block_manager.clone(),
                         trash_controller.clone(),
                         event,
                     )

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

@@ -1,5 +1,5 @@
 use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
-use flowy_collaboration::{client_document::default::initial_delta_string, entities::revision::RevisionState};
+use flowy_collaboration::{client_document::default::initial_quill_delta_string, entities::revision::RevisionState};
 use flowy_folder::entities::workspace::CreateWorkspacePayload;
 use flowy_test::{event_builder::*, FlowySDKTest};
 
@@ -175,7 +175,7 @@ async fn open_document_view() {
 
     test.run_scripts(vec![OpenDocument]).await;
     let document_info = test.document_info.unwrap();
-    assert_eq!(document_info.text, initial_delta_string());
+    assert_eq!(document_info.text, initial_quill_delta_string());
 }
 
 #[tokio::test]

+ 1 - 1
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -30,7 +30,7 @@ uuid = { version = "0.8", features = ["serde", "v4"] }
 bytes = { version = "1.0" }
 diesel = {version = "1.4.8", features = ["sqlite"]}
 dashmap = "4.0"
-
+tokio = {version = "1", features = ["sync"]}
 
 parking_lot = "0.11"
 

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

@@ -3,53 +3,47 @@ use flowy_error::FlowyError;
 use flowy_grid_data_model::entities::{
     CreateGridPayload, Grid, GridId, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder,
 };
-use lib_dispatch::prelude::{AppData, Data, DataResult};
+use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
 
-#[tracing::instrument(skip(data, controller), err)]
-pub(crate) async fn create_grid_handler(
-    data: Data<CreateGridPayload>,
-    controller: AppData<Arc<GridManager>>,
-) -> DataResult<Grid, FlowyError> {
-    todo!()
-}
-
-#[tracing::instrument(skip(data, controller), err)]
+#[tracing::instrument(skip(data, manager), err)]
 pub(crate) async fn open_grid_handler(
     data: Data<GridId>,
-    controller: AppData<Arc<GridManager>>,
+    manager: AppData<Arc<GridManager>>,
 ) -> DataResult<Grid, FlowyError> {
-    let _params: GridId = data.into_inner();
-
-    todo!()
+    let grid_id: GridId = data.into_inner();
+    let editor = manager.open_grid(grid_id).await?;
+    let grid = editor.grid_data().await;
+    data_result(grid)
 }
 
-#[tracing::instrument(skip(data, controller), err)]
+#[tracing::instrument(skip(data, manager), err)]
 pub(crate) async fn get_rows_handler(
     data: Data<RepeatedRowOrder>,
-    controller: AppData<Arc<GridManager>>,
+    manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RepeatedRow, FlowyError> {
     let row_orders: RepeatedRowOrder = data.into_inner();
-
-    todo!()
+    let repeated_row = manager.get_rows(row_orders).await;
+    data_result(repeated_row)
 }
 
-#[tracing::instrument(skip(data, controller), err)]
+#[tracing::instrument(skip(data, manager), err)]
 pub(crate) async fn get_fields_handler(
     data: Data<RepeatedFieldOrder>,
-    controller: AppData<Arc<GridManager>>,
+    manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RepeatedField, FlowyError> {
     let field_orders: RepeatedFieldOrder = data.into_inner();
-
-    todo!()
+    let repeated_field = manager.get_fields(field_orders).await;
+    data_result(repeated_field)
 }
 
-#[tracing::instrument(skip(data, controller), err)]
+#[tracing::instrument(skip(data, manager), err)]
 pub(crate) async fn create_row_handler(
     data: Data<GridId>,
-    controller: AppData<Arc<GridManager>>,
+    manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let id: GridId = data.into_inner();
-
+    let editor = manager.get_grid_editor(id.as_ref())?;
+    let _ = editor.create_empty_row().await?;
     Ok(())
 }

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

@@ -8,7 +8,6 @@ use strum_macros::Display;
 pub fn create(grid_manager: Arc<GridManager>) -> Module {
     let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(grid_manager);
     module = module
-        .event(GridEvent::CreateGrid, create_grid_handler)
         .event(GridEvent::OpenGrid, open_grid_handler)
         .event(GridEvent::GetRows, get_rows_handler)
         .event(GridEvent::GetFields, get_fields_handler)
@@ -20,18 +19,15 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
 #[event_err = "FlowyError"]
 pub enum GridEvent {
-    #[event(input = "CreateGridPayload", output = "Grid")]
-    CreateGrid = 0,
-
     #[event(input = "GridId", output = "Grid")]
-    OpenGrid = 1,
+    OpenGrid = 0,
 
     #[event(input = "RepeatedRowOrder", output = "RepeatedRow")]
-    GetRows = 2,
+    GetRows = 1,
 
     #[event(input = "RepeatedFieldOrder", output = "RepeatedField")]
-    GetFields = 3,
+    GetFields = 2,
 
     #[event(input = "GridId")]
-    CreateRow = 4,
+    CreateRow = 3,
 }

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

@@ -1,8 +1,15 @@
 use crate::services::grid_editor::ClientGridEditor;
+use crate::services::kv_persistence::GridKVPersistence;
 use dashmap::DashMap;
+use flowy_collaboration::client_grid::{make_grid_delta, make_grid_revisions};
+use flowy_collaboration::entities::revision::RepeatedRevision;
 use flowy_error::{FlowyError, FlowyResult};
+use flowy_grid_data_model::entities::{
+    Field, FieldOrder, Grid, RawRow, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, RowOrder,
+};
 use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket};
 use lib_sqlite::ConnectionPool;
+use parking_lot::RwLock;
 use std::sync::Arc;
 
 pub trait GridUser: Send + Sync {
@@ -15,27 +22,63 @@ pub struct GridManager {
     grid_editors: Arc<GridEditors>,
     grid_user: Arc<dyn GridUser>,
     rev_web_socket: Arc<dyn RevisionWebSocket>,
+    kv_persistence: Arc<RwLock<Option<Arc<GridKVPersistence>>>>,
 }
 
 impl GridManager {
     pub fn new(grid_user: Arc<dyn GridUser>, rev_web_socket: Arc<dyn RevisionWebSocket>) -> Self {
         let grid_editors = Arc::new(GridEditors::new());
+
+        // kv_persistence will be initialized after first access.
+        // See get_kv_persistence function below
+        let kv_persistence = Arc::new(RwLock::new(None));
         Self {
             grid_editors,
             grid_user,
             rev_web_socket,
+            kv_persistence,
+        }
+    }
+
+    #[tracing::instrument(level = "debug", skip_all, err)]
+    pub async fn create_grid<T: AsRef<str>>(
+        &self,
+        grid_id: T,
+        fields: Option<Vec<Field>>,
+        rows: Option<Vec<RawRow>>,
+    ) -> FlowyResult<()> {
+        let grid_id = grid_id.as_ref();
+        let user_id = self.grid_user.user_id()?;
+        let mut field_orders = vec![];
+        let mut row_orders = vec![];
+        if let Some(fields) = fields {
+            field_orders = fields.iter().map(|field| FieldOrder::from(field)).collect::<Vec<_>>();
         }
+        if let Some(rows) = rows {
+            row_orders = rows.iter().map(|row| RowOrder::from(row)).collect::<Vec<_>>();
+        }
+
+        let grid = Grid {
+            id: grid_id.to_owned(),
+            field_orders: field_orders.into(),
+            row_orders: row_orders.into(),
+        };
+        let revisions = make_grid_revisions(&user_id, &grid);
+        let db_pool = self.grid_user.db_pool()?;
+        let rev_manager = self.make_grid_rev_manager(grid_id, db_pool)?;
+        let _ = rev_manager.reset_object(revisions).await?;
+        Ok(())
     }
 
-    #[tracing::instrument(level = "debug", skip(self, grid_id), fields(grid_id), err)]
-    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<Arc<ClientGridEditor>, FlowyError> {
+    #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
+    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<ClientGridEditor>> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
-        self.get_grid_editor(grid_id).await
+        self.get_or_create_grid_editor(grid_id).await
     }
 
-    #[tracing::instrument(level = "trace", skip(self, grid_id), fields(grid_id), err)]
-    pub fn close_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<(), FlowyError> {
+    #[tracing::instrument(level = "trace", skip_all, fields(grid_id), err)]
+    pub fn close_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
         self.grid_editors.remove(grid_id);
@@ -43,14 +86,29 @@ impl GridManager {
     }
 
     #[tracing::instrument(level = "debug", skip(self, grid_id), fields(doc_id), err)]
-    pub fn delete_grid<T: AsRef<str>>(&self, grid_id: T) -> Result<(), FlowyError> {
+    pub fn delete_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<()> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
         self.grid_editors.remove(grid_id);
         Ok(())
     }
 
-    async fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
+    pub async fn get_rows(&self, row_orders: RepeatedRowOrder) -> RepeatedRow {
+        todo!()
+    }
+
+    pub async fn get_fields(&self, field_orders: RepeatedFieldOrder) -> RepeatedField {
+        todo!()
+    }
+
+    pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
+        match self.grid_editors.get(grid_id) {
+            None => Err(FlowyError::internal().context("Should call open_grid function first")),
+            Some(editor) => Ok(editor),
+        }
+    }
+
+    async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
         match self.grid_editors.get(grid_id) {
             None => {
                 let db_pool = self.grid_user.db_pool()?;
@@ -65,12 +123,33 @@ impl GridManager {
         grid_id: &str,
         pool: Arc<ConnectionPool>,
     ) -> Result<Arc<ClientGridEditor>, FlowyError> {
-        let token = self.grid_user.token()?;
-        let user_id = self.grid_user.user_id()?;
-        let grid_editor = ClientGridEditor::new(&user_id, grid_id, &token, pool, self.rev_web_socket.clone()).await?;
+        let user = self.grid_user.clone();
+        let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
+        let kv_persistence = self.get_kv_persistence()?;
+        let grid_editor = ClientGridEditor::new(grid_id, user, rev_manager, kv_persistence).await?;
         self.grid_editors.insert(grid_id, &grid_editor);
         Ok(grid_editor)
     }
+
+    fn make_grid_rev_manager(&self, grid_id: &str, pool: Arc<ConnectionPool>) -> FlowyResult<RevisionManager> {
+        let user_id = self.grid_user.user_id()?;
+        let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, grid_id, pool));
+        let rev_manager = RevisionManager::new(&user_id, grid_id, rev_persistence);
+        Ok(rev_manager)
+    }
+
+    fn get_kv_persistence(&self) -> FlowyResult<Arc<GridKVPersistence>> {
+        let read_guard = self.kv_persistence.read();
+        if read_guard.is_some() {
+            return Ok(read_guard.clone().unwrap());
+        }
+        drop(read_guard);
+
+        let pool = self.grid_user.db_pool()?;
+        let kv_persistence = Arc::new(GridKVPersistence::new(pool));
+        *self.kv_persistence.write() = Some(kv_persistence.clone());
+        Ok(kv_persistence)
+    }
 }
 
 pub struct GridEditors {

+ 12 - 15
frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs

@@ -25,11 +25,10 @@
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
 pub enum GridEvent {
-    CreateGrid = 0,
-    OpenGrid = 1,
-    GetRows = 2,
-    GetFields = 3,
-    CreateRow = 4,
+    OpenGrid = 0,
+    GetRows = 1,
+    GetFields = 2,
+    CreateRow = 3,
 }
 
 impl ::protobuf::ProtobufEnum for GridEvent {
@@ -39,18 +38,16 @@ impl ::protobuf::ProtobufEnum for GridEvent {
 
     fn from_i32(value: i32) -> ::std::option::Option<GridEvent> {
         match value {
-            0 => ::std::option::Option::Some(GridEvent::CreateGrid),
-            1 => ::std::option::Option::Some(GridEvent::OpenGrid),
-            2 => ::std::option::Option::Some(GridEvent::GetRows),
-            3 => ::std::option::Option::Some(GridEvent::GetFields),
-            4 => ::std::option::Option::Some(GridEvent::CreateRow),
+            0 => ::std::option::Option::Some(GridEvent::OpenGrid),
+            1 => ::std::option::Option::Some(GridEvent::GetRows),
+            2 => ::std::option::Option::Some(GridEvent::GetFields),
+            3 => ::std::option::Option::Some(GridEvent::CreateRow),
             _ => ::std::option::Option::None
         }
     }
 
     fn values() -> &'static [Self] {
         static values: &'static [GridEvent] = &[
-            GridEvent::CreateGrid,
             GridEvent::OpenGrid,
             GridEvent::GetRows,
             GridEvent::GetFields,
@@ -72,7 +69,7 @@ impl ::std::marker::Copy for GridEvent {
 
 impl ::std::default::Default for GridEvent {
     fn default() -> Self {
-        GridEvent::CreateGrid
+        GridEvent::OpenGrid
     }
 }
 
@@ -83,9 +80,9 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*T\n\tGridEvent\x12\x0e\n\nCreateGrid\x10\0\x12\x0c\
-    \n\x08OpenGrid\x10\x01\x12\x0b\n\x07GetRows\x10\x02\x12\r\n\tGetFields\
-    \x10\x03\x12\r\n\tCreateRow\x10\x04b\x06proto3\
+    \n\x0fevent_map.proto*D\n\tGridEvent\x12\x0c\n\x08OpenGrid\x10\0\x12\x0b\
+    \n\x07GetRows\x10\x01\x12\r\n\tGetFields\x10\x02\x12\r\n\tCreateRow\x10\
+    \x03b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 4 - 5
frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto

@@ -1,9 +1,8 @@
 syntax = "proto3";
 
 enum GridEvent {
-    CreateGrid = 0;
-    OpenGrid = 1;
-    GetRows = 2;
-    GetFields = 3;
-    CreateRow = 4;
+    OpenGrid = 0;
+    GetRows = 1;
+    GetFields = 2;
+    CreateRow = 3;
 }

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

@@ -1,60 +1,64 @@
+use crate::manager::GridUser;
 use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
 use flowy_collaboration::client_grid::{GridChange, GridPad};
 use flowy_collaboration::entities::revision::Revision;
 use flowy_collaboration::util::make_delta_from_revisions;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::entities::{Field, GridId, RawRow};
+use flowy_grid_data_model::entities::{Field, Grid, GridId, RawRow};
 use flowy_sync::{
     RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
     RevisionWebSocket, RevisionWebSocketManager,
 };
 use lib_infra::future::FutureResult;
+use lib_infra::uuid;
 use lib_ot::core::PlainTextAttributes;
 use lib_sqlite::ConnectionPool;
-use parking_lot::RwLock;
 use std::sync::Arc;
+use tokio::sync::RwLock;
 
 pub struct ClientGridEditor {
-    user_id: String,
     grid_id: String,
-    grid: Arc<RwLock<GridPad>>,
+    user: Arc<dyn GridUser>,
+    grid_pad: Arc<RwLock<GridPad>>,
     rev_manager: Arc<RevisionManager>,
-    kv: Arc<GridKVPersistence>,
+    kv_persistence: Arc<GridKVPersistence>,
 }
 
 impl ClientGridEditor {
     pub async fn new(
-        user_id: &str,
         grid_id: &str,
-        token: &str,
-        pool: Arc<ConnectionPool>,
-        _web_socket: Arc<dyn RevisionWebSocket>,
+        user: Arc<dyn GridUser>,
+        mut rev_manager: RevisionManager,
+        kv_persistence: Arc<GridKVPersistence>,
     ) -> FlowyResult<Arc<Self>> {
-        let rev_persistence = Arc::new(RevisionPersistence::new(user_id, grid_id, pool.clone()));
-        let mut rev_manager = RevisionManager::new(user_id, grid_id, rev_persistence);
-        let cloud = Arc::new(GridRevisionCloudService {
-            token: token.to_string(),
-        });
-        let grid = Arc::new(RwLock::new(
+        let token = user.token()?;
+        let cloud = Arc::new(GridRevisionCloudService { token });
+        let grid_pad = Arc::new(RwLock::new(
             rev_manager.load::<GridPadBuilder, GridRevisionCompact>(cloud).await?,
         ));
         let rev_manager = Arc::new(rev_manager);
-        let kv = Arc::new(GridKVPersistence::new(pool));
-
-        let user_id = user_id.to_owned();
-        let grid_id = grid_id.to_owned();
         Ok(Arc::new(Self {
-            user_id,
-            grid_id,
-            grid,
+            grid_id: grid_id.to_owned(),
+            user,
+            grid_pad,
             rev_manager,
-            kv,
+            kv_persistence,
         }))
     }
 
-    pub async fn create_row(&self, row: RawRow) -> FlowyResult<()> {
+    pub async fn create_empty_row(&self) -> FlowyResult<()> {
+        let row = RawRow {
+            id: uuid(),
+            grid_id: self.grid_id.clone(),
+            cell_by_field_id: Default::default(),
+        };
+        self.create_row(row).await?;
+        Ok(())
+    }
+
+    async fn create_row(&self, row: RawRow) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.create_row(&row)?)).await?;
-        let _ = self.kv.set(row)?;
+        let _ = self.kv_persistence.set(row)?;
         Ok(())
     }
 
@@ -66,7 +70,7 @@ impl ClientGridEditor {
 
     pub async fn create_field(&mut self, field: Field) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.create_field(&field)?)).await?;
-        let _ = self.kv.set(field)?;
+        let _ = self.kv_persistence.set(field)?;
         Ok(())
     }
 
@@ -76,11 +80,15 @@ impl ClientGridEditor {
         Ok(())
     }
 
+    pub async fn grid_data(&self) -> Grid {
+        self.grid_pad.read().await.grid_data()
+    }
+
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
-        F: FnOnce(&mut GridPad) -> FlowyResult<Option<GridChange>>,
+        F: for<'a> FnOnce(&'a mut GridPad) -> FlowyResult<Option<GridChange>>,
     {
-        let mut write_guard = self.grid.write();
+        let mut write_guard = self.grid_pad.write().await;
         match f(&mut *write_guard)? {
             None => {}
             Some(change) => {
@@ -92,6 +100,7 @@ impl ClientGridEditor {
 
     async fn apply_change(&self, change: GridChange) -> FlowyResult<()> {
         let GridChange { delta, md5 } = change;
+        let user_id = self.user.user_id()?;
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
         let delta_data = delta.to_bytes();
         let revision = Revision::new(
@@ -99,7 +108,7 @@ impl ClientGridEditor {
             base_rev_id,
             rev_id,
             delta_data,
-            &self.user_id,
+            &user_id,
             md5,
         );
         let _ = self
@@ -114,8 +123,8 @@ struct GridPadBuilder();
 impl RevisionObjectBuilder for GridPadBuilder {
     type Output = GridPad;
 
-    fn build_object(_object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
-        let pad = GridPad::from_revisions(revisions)?;
+    fn build_object(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
+        let pad = GridPad::from_revisions(object_id, revisions)?;
         Ok(pad)
     }
 }

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

@@ -3,6 +3,6 @@ mod util;
 
 pub mod cell_data;
 pub mod grid_editor;
-mod kv_persistence;
+pub mod kv_persistence;
 
 pub use stringify::*;

+ 2 - 2
frontend/rust-lib/flowy-net/src/local_server/server.rs

@@ -2,7 +2,7 @@ use crate::local_server::persistence::LocalDocumentCloudPersistence;
 use async_stream::stream;
 use bytes::Bytes;
 use flowy_collaboration::{
-    client_document::default::initial_delta_string,
+    client_document::default::initial_quill_delta_string,
     entities::{
         document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams},
         ws_data::{ClientRevisionWSData, ClientRevisionWSDataType},
@@ -420,7 +420,7 @@ impl BlockCloudService for LocalServer {
     fn read_block(&self, _token: &str, params: BlockId) -> FutureResult<Option<BlockInfo>, FlowyError> {
         let doc = BlockInfo {
             block_id: params.value,
-            text: initial_delta_string(),
+            text: initial_quill_delta_string(),
             rev_id: 0,
             base_rev_id: 0,
         };

+ 3 - 3
shared-lib/flowy-collaboration/src/client_document/default/mod.rs

@@ -1,13 +1,13 @@
 use lib_ot::{core::DeltaBuilder, rich_text::RichTextDelta};
 
 #[inline]
-pub fn initial_delta() -> RichTextDelta {
+pub fn initial_quill_delta() -> RichTextDelta {
     DeltaBuilder::new().insert("\n").build()
 }
 
 #[inline]
-pub fn initial_delta_string() -> String {
-    initial_delta().to_delta_json()
+pub fn initial_quill_delta_string() -> String {
+    initial_quill_delta().to_delta_json()
 }
 
 #[inline]

+ 2 - 2
shared-lib/flowy-collaboration/src/client_document/document_pad.rs

@@ -1,6 +1,6 @@
 use crate::{
     client_document::{
-        default::initial_delta,
+        default::initial_quill_delta,
         history::{History, UndoResult},
         view::{ViewExtensions, RECORD_THRESHOLD},
     },
@@ -26,7 +26,7 @@ impl InitialDocumentText for PlainDoc {
 pub struct NewlineDoc();
 impl InitialDocumentText for NewlineDoc {
     fn initial_delta() -> RichTextDelta {
-        initial_delta()
+        initial_quill_delta()
     }
 }
 

+ 16 - 4
shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs

@@ -1,4 +1,4 @@
-use crate::entities::revision::{md5, Revision};
+use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{internal_error, CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
 use flowy_grid_data_model::entities::{CellChangeset, Field, FieldOrder, Grid, RawRow, RowOrder};
@@ -26,7 +26,7 @@ impl GridPad {
         })
     }
 
-    pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
+    pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
         let folder_delta: GridDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
         Self::from_delta(folder_delta)
     }
@@ -81,6 +81,11 @@ impl GridPad {
         md5(&self.delta.to_bytes())
     }
 
+    pub fn grid_data(&self) -> Grid {
+        let grid_ref: &Grid = &self.grid;
+        grid_ref.clone()
+    }
+
     pub fn modify_grid<F>(&mut self, f: F) -> CollaborateResult<Option<GridChange>>
     where
         F: FnOnce(&mut Grid) -> CollaborateResult<Option<()>>,
@@ -115,11 +120,18 @@ pub struct GridChange {
     pub md5: String,
 }
 
-pub fn default_grid_delta(grid: &Grid) -> GridDelta {
+pub fn make_grid_delta(grid: &Grid) -> GridDelta {
     let json = serde_json::to_string(&grid).unwrap();
     PlainTextDeltaBuilder::new().insert(&json).build()
 }
 
+pub fn make_grid_revisions(user_id: &str, grid: &Grid) -> RepeatedRevision {
+    let delta = make_grid_delta(grid);
+    let bytes = delta.to_bytes();
+    let revision = Revision::initial_revision(user_id, &grid.id, bytes);
+    revision.into()
+}
+
 impl std::default::Default for GridPad {
     fn default() -> Self {
         let grid = Grid {
@@ -127,7 +139,7 @@ impl std::default::Default for GridPad {
             field_orders: Default::default(),
             row_orders: Default::default(),
         };
-        let delta = default_grid_delta(&grid);
+        let delta = make_grid_delta(&grid);
         GridPad {
             grid: Arc::new(grid),
             delta,

+ 6 - 6
shared-lib/flowy-collaboration/src/entities/revision.rs

@@ -82,12 +82,6 @@ impl Revision {
     }
 }
 
-impl std::convert::From<Revision> for RepeatedRevision {
-    fn from(revision: Revision) -> Self {
-        RepeatedRevision { items: vec![revision] }
-    }
-}
-
 impl std::fmt::Debug for Revision {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         let _ = f.write_fmt(format_args!("object_id {}, ", self.object_id))?;
@@ -125,6 +119,12 @@ impl std::ops::DerefMut for RepeatedRevision {
     }
 }
 
+impl std::convert::From<Revision> for RepeatedRevision {
+    fn from(revision: Revision) -> Self {
+        Self { items: vec![revision] }
+    }
+}
+
 impl RepeatedRevision {
     pub fn new(mut items: Vec<Revision>) -> Self {
         items.sort_by(|a, b| a.rev_id.cmp(&b.rev_id));

+ 2 - 0
shared-lib/flowy-folder-data-model/src/entities/view.rs

@@ -85,6 +85,7 @@ impl std::convert::From<View> for Trash {
 pub enum ViewDataType {
     RichText = 0,
     PlainText = 1,
+    Grid = 2,
 }
 
 impl std::default::Default for ViewDataType {
@@ -98,6 +99,7 @@ impl std::convert::From<i32> for ViewDataType {
         match val {
             0 => ViewDataType::RichText,
             1 => ViewDataType::PlainText,
+            2 => ViewDataType::Grid,
             _ => {
                 log::error!("Invalid view type: {}", val);
                 ViewDataType::PlainText

+ 6 - 3
shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs

@@ -2823,6 +2823,7 @@ impl ::protobuf::reflect::ProtobufValue for UpdateViewParams {
 pub enum ViewDataType {
     RichText = 0,
     PlainText = 1,
+    Grid = 2,
 }
 
 impl ::protobuf::ProtobufEnum for ViewDataType {
@@ -2834,6 +2835,7 @@ impl ::protobuf::ProtobufEnum for ViewDataType {
         match value {
             0 => ::std::option::Option::Some(ViewDataType::RichText),
             1 => ::std::option::Option::Some(ViewDataType::PlainText),
+            2 => ::std::option::Option::Some(ViewDataType::Grid),
             _ => ::std::option::Option::None
         }
     }
@@ -2842,6 +2844,7 @@ impl ::protobuf::ProtobufEnum for ViewDataType {
         static values: &'static [ViewDataType] = &[
             ViewDataType::RichText,
             ViewDataType::PlainText,
+            ViewDataType::Grid,
         ];
         values
     }
@@ -2904,9 +2907,9 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\
     \x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\
     \x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\
-    \x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail*+\n\x0cVi\
-    ewDataType\x12\x0c\n\x08RichText\x10\0\x12\r\n\tPlainText\x10\x01b\x06pr\
-    oto3\
+    \x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnail*5\n\x0cVi\
+    ewDataType\x12\x0c\n\x08RichText\x10\0\x12\r\n\tPlainText\x10\x01\x12\
+    \x08\n\x04Grid\x10\x02b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 1 - 0
shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto

@@ -58,4 +58,5 @@ message UpdateViewParams {
 enum ViewDataType {
     RichText = 0;
     PlainText = 1;
+    Grid = 2;
 }

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

@@ -26,6 +26,15 @@ pub struct FieldOrder {
     pub visibility: bool,
 }
 
+impl std::convert::From<&Field> for FieldOrder {
+    fn from(field: &Field) -> Self {
+        Self {
+            field_id: field.id.clone(),
+            visibility: true,
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, ProtoBuf)]
 pub struct RepeatedFieldOrder {
     #[pb(index = 1)]
@@ -33,6 +42,12 @@ pub struct RepeatedFieldOrder {
     pub items: Vec<FieldOrder>,
 }
 
+impl std::convert::From<Vec<FieldOrder>> for RepeatedFieldOrder {
+    fn from(items: Vec<FieldOrder>) -> Self {
+        Self { items }
+    }
+}
+
 impl std::ops::Deref for RepeatedFieldOrder {
     type Target = Vec<FieldOrder>;
 
@@ -76,6 +91,18 @@ pub struct RepeatedField {
     #[pb(index = 1)]
     pub items: Vec<Field>,
 }
+impl std::ops::Deref for RepeatedField {
+    type Target = Vec<Field>;
+    fn deref(&self) -> &Self::Target {
+        &self.items
+    }
+}
+
+impl std::ops::DerefMut for RepeatedField {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.items
+    }
+}
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumString, EnumIter, Display, Serialize, Deserialize)]
 pub enum FieldType {
@@ -156,6 +183,16 @@ pub struct RowOrder {
     pub visibility: bool,
 }
 
+impl std::convert::From<&RawRow> for RowOrder {
+    fn from(row: &RawRow) -> Self {
+        Self {
+            grid_id: row.grid_id.clone(),
+            row_id: row.id.clone(),
+            visibility: true,
+        }
+    }
+}
+
 #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
 pub struct RepeatedRowOrder {
     #[pb(index = 1)]
@@ -163,9 +200,14 @@ pub struct RepeatedRowOrder {
     pub items: Vec<RowOrder>,
 }
 
+impl std::convert::From<Vec<RowOrder>> for RepeatedRowOrder {
+    fn from(items: Vec<RowOrder>) -> Self {
+        Self { items }
+    }
+}
+
 impl std::ops::Deref for RepeatedRowOrder {
     type Target = Vec<RowOrder>;
-
     fn deref(&self) -> &Self::Target {
         &self.items
     }
@@ -210,6 +252,19 @@ pub struct RepeatedRow {
     pub items: Vec<Row>,
 }
 
+impl std::ops::Deref for RepeatedRow {
+    type Target = Vec<Row>;
+    fn deref(&self) -> &Self::Target {
+        &self.items
+    }
+}
+
+impl std::ops::DerefMut for RepeatedRow {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.items
+    }
+}
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct Row {
     #[pb(index = 1)]