diff --git a/frontend/app_flowy/lib/plugin/plugin.dart b/frontend/app_flowy/lib/plugin/plugin.dart index 5007e92e18..c9f7d2abd1 100644 --- a/frontend/app_flowy/lib/plugin/plugin.dart +++ b/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; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart index 72465dd54b..b5b368fd72 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart +++ b/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; diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart index 21e2804539..52a51cd026 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart +++ b/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> 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); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbenum.dart index 452830c084..400c1d579a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbenum.dart +++ b/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 values = [ RichText, PlainText, + Grid, ]; static final $core.Map<$core.int, ViewDataType> _byValue = $pb.ProtobufEnum.initByValue(values); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart index 8c50bd08fc..8cb2913dfe 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart +++ b/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', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart index 2b523834c3..7877beefde 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart +++ b/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 values = [ - CreateGrid, OpenGrid, GetRows, GetFields, diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart index 19412ddc1d..96b422a379 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart +++ b/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='); diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index f5ae68cbda..1a8fce9353 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1071,6 +1071,7 @@ dependencies = [ "rusty-money", "strum", "strum_macros", + "tokio", "tracing", "uuid", ] diff --git a/frontend/rust-lib/flowy-block/src/manager.rs b/frontend/rust-lib/flowy-block/src/manager.rs index 6c9e23b4ed..d41edf045d 100644 --- a/frontend/rust-lib/flowy-block/src/manager.rs +++ b/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, 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) -> Result { + fn make_block_rev_manager(&self, doc_id: &str, pool: Arc) -> Result { 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)) diff --git a/frontend/rust-lib/flowy-folder/src/controller.rs b/frontend/rust-lib/flowy-folder/src/controller.rs index a01a26b7d5..528a602d57 100644 --- a/frontend/rust-lib/flowy-folder/src/controller.rs +++ b/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![])?; diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs index 955bf44818..d87b761a7f 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs +++ b/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 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 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 diff --git a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs index 32e4aa5136..57cdeba7d1 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/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 { 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, ¶ms.view_id, delta_data).into(); - let _ = self.create_view(¶ms.view_id, repeated_revision).await?; + let _ = self.create_view(¶ms.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, ) diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs b/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs index 76b0aaf9c6..18a11d9a0b 100644 --- a/frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs +++ b/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] diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index 5c65fc5ebc..331dd03b9e 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/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" diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 7d6e7f8e21..e1201408d4 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/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, - controller: AppData>, -) -> DataResult { - todo!() -} - -#[tracing::instrument(skip(data, controller), err)] +#[tracing::instrument(skip(data, manager), err)] pub(crate) async fn open_grid_handler( data: Data, - controller: AppData>, + manager: AppData>, ) -> DataResult { - 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, - controller: AppData>, + manager: AppData>, ) -> DataResult { 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, - controller: AppData>, + manager: AppData>, ) -> DataResult { 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, - controller: AppData>, + manager: AppData>, ) -> Result<(), FlowyError> { let id: GridId = data.into_inner(); - + let editor = manager.get_grid_editor(id.as_ref())?; + let _ = editor.create_empty_row().await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index 787cd6336d..bd52c9f0f4 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -8,7 +8,6 @@ use strum_macros::Display; pub fn create(grid_manager: Arc) -> 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) -> 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, } diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index af4de88293..694ead2d55 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/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, grid_user: Arc, rev_web_socket: Arc, + kv_persistence: Arc>>>, } impl GridManager { pub fn new(grid_user: Arc, rev_web_socket: Arc) -> 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(self, grid_id), fields(grid_id), err)] - pub async fn open_grid>(&self, grid_id: T) -> Result, FlowyError> { + #[tracing::instrument(level = "debug", skip_all, err)] + pub async fn create_grid>( + &self, + grid_id: T, + fields: Option>, + rows: Option>, + ) -> FlowyResult<()> { let grid_id = grid_id.as_ref(); - tracing::Span::current().record("grid_id", &grid_id); - self.get_grid_editor(grid_id).await + 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::>(); + } + if let Some(rows) = rows { + row_orders = rows.iter().map(|row| RowOrder::from(row)).collect::>(); + } + + 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 = "trace", skip(self, grid_id), fields(grid_id), err)] - pub fn close_grid>(&self, grid_id: T) -> Result<(), FlowyError> { + #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)] + pub async fn open_grid>(&self, grid_id: T) -> FlowyResult> { + let grid_id = grid_id.as_ref(); + tracing::Span::current().record("grid_id", &grid_id); + self.get_or_create_grid_editor(grid_id).await + } + + #[tracing::instrument(level = "trace", skip_all, fields(grid_id), err)] + pub fn close_grid>(&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>(&self, grid_id: T) -> Result<(), FlowyError> { + pub fn delete_grid>(&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> { + 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> { + 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> { 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, ) -> Result, 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) -> FlowyResult { + 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> { + 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 { diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs index b2456ff055..ec00eaf71c 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs +++ b/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 { 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; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto index 82819b6bd5..d0b33de419 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto +++ b/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; } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 974c026069..4a60254ca0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/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>, + user: Arc, + grid_pad: Arc>, rev_manager: Arc, - kv: Arc, + kv_persistence: Arc, } impl ClientGridEditor { pub async fn new( - user_id: &str, grid_id: &str, - token: &str, - pool: Arc, - _web_socket: Arc, + user: Arc, + mut rev_manager: RevisionManager, + kv_persistence: Arc, ) -> FlowyResult> { - 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::(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(&self, f: F) -> FlowyResult<()> where - F: FnOnce(&mut GridPad) -> FlowyResult>, + F: for<'a> FnOnce(&'a mut GridPad) -> FlowyResult>, { - 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) -> FlowyResult { - let pad = GridPad::from_revisions(revisions)?; + fn build_object(object_id: &str, revisions: Vec) -> FlowyResult { + let pad = GridPad::from_revisions(object_id, revisions)?; Ok(pad) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/mod.rs b/frontend/rust-lib/flowy-grid/src/services/mod.rs index 0d9cae4f1c..f62472a0e8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/mod.rs +++ b/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::*; diff --git a/frontend/rust-lib/flowy-net/src/local_server/server.rs b/frontend/rust-lib/flowy-net/src/local_server/server.rs index a9149720fb..3bfa30e06a 100644 --- a/frontend/rust-lib/flowy-net/src/local_server/server.rs +++ b/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, FlowyError> { let doc = BlockInfo { block_id: params.value, - text: initial_delta_string(), + text: initial_quill_delta_string(), rev_id: 0, base_rev_id: 0, }; diff --git a/shared-lib/flowy-collaboration/src/client_document/default/mod.rs b/shared-lib/flowy-collaboration/src/client_document/default/mod.rs index 35f674e41d..0466093bda 100644 --- a/shared-lib/flowy-collaboration/src/client_document/default/mod.rs +++ b/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] diff --git a/shared-lib/flowy-collaboration/src/client_document/document_pad.rs b/shared-lib/flowy-collaboration/src/client_document/document_pad.rs index 99a86769ce..9f3bbe434b 100644 --- a/shared-lib/flowy-collaboration/src/client_document/document_pad.rs +++ b/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() } } diff --git a/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs b/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs index 0d6eab93f2..4cc10614d7 100644 --- a/shared-lib/flowy-collaboration/src/client_grid/grid_pad.rs +++ b/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) -> CollaborateResult { + pub fn from_revisions(_grid_id: &str, revisions: Vec) -> CollaborateResult { let folder_delta: GridDelta = make_delta_from_revisions::(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(&mut self, f: F) -> CollaborateResult> where F: FnOnce(&mut Grid) -> CollaborateResult>, @@ -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, diff --git a/shared-lib/flowy-collaboration/src/entities/revision.rs b/shared-lib/flowy-collaboration/src/entities/revision.rs index c85ba937fb..48853fa9ed 100644 --- a/shared-lib/flowy-collaboration/src/entities/revision.rs +++ b/shared-lib/flowy-collaboration/src/entities/revision.rs @@ -82,12 +82,6 @@ impl Revision { } } -impl std::convert::From 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 for RepeatedRevision { + fn from(revision: Revision) -> Self { + Self { items: vec![revision] } + } +} + impl RepeatedRevision { pub fn new(mut items: Vec) -> Self { items.sort_by(|a, b| a.rev_id.cmp(&b.rev_id)); diff --git a/shared-lib/flowy-folder-data-model/src/entities/view.rs b/shared-lib/flowy-folder-data-model/src/entities/view.rs index a16a384be1..53a6bd8caa 100644 --- a/shared-lib/flowy-folder-data-model/src/entities/view.rs +++ b/shared-lib/flowy-folder-data-model/src/entities/view.rs @@ -85,6 +85,7 @@ impl std::convert::From 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 for ViewDataType { match val { 0 => ViewDataType::RichText, 1 => ViewDataType::PlainText, + 2 => ViewDataType::Grid, _ => { log::error!("Invalid view type: {}", val); ViewDataType::PlainText diff --git a/shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs b/shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs index 0e1e3a7279..ddf9020243 100644 --- a/shared-lib/flowy-folder-data-model/src/protobuf/model/view.rs +++ b/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; diff --git a/shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto b/shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto index b938e004f4..af05749a14 100644 --- a/shared-lib/flowy-folder-data-model/src/protobuf/proto/view.proto +++ b/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; } diff --git a/shared-lib/flowy-grid-data-model/src/entities/grid.rs b/shared-lib/flowy-grid-data-model/src/entities/grid.rs index 92ca8006e7..6d070e6893 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/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, } +impl std::convert::From> for RepeatedFieldOrder { + fn from(items: Vec) -> Self { + Self { items } + } +} + impl std::ops::Deref for RepeatedFieldOrder { type Target = Vec; @@ -76,6 +91,18 @@ pub struct RepeatedField { #[pb(index = 1)] pub items: Vec, } +impl std::ops::Deref for RepeatedField { + type Target = Vec; + 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, } +impl std::convert::From> for RepeatedRowOrder { + fn from(items: Vec) -> Self { + Self { items } + } +} + impl std::ops::Deref for RepeatedRowOrder { type Target = Vec; - fn deref(&self) -> &Self::Target { &self.items } @@ -210,6 +252,19 @@ pub struct RepeatedRow { pub items: Vec, } +impl std::ops::Deref for RepeatedRow { + type Target = Vec; + 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)]