diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 868e04a211..fba225a3a3 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -935,6 +935,7 @@ dependencies = [ "atomic_refcell", "bytes", "chrono", + "crossbeam-utils", "dart-notify", "dashmap", "diesel", diff --git a/frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql b/frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql index f1119aa25a..b125f69360 100644 --- a/frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql +++ b/frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE user_table; \ No newline at end of file +ALTER TABLE user_table DROP COLUMN workspace; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql b/frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql index 291a97c5ce..df82040abc 100644 --- a/frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql +++ b/frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql @@ -1 +1,2 @@ --- This file should undo anything in `up.sql` \ No newline at end of file +-- This file should undo anything in `up.sql` +ALTER TABLE view_table DROP COLUMN ext_data; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/down.sql b/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/down.sql new file mode 100644 index 0000000000..aff09f3bb9 --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE grid_view_rev_table; \ No newline at end of file diff --git a/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/up.sql b/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/up.sql new file mode 100644 index 0000000000..e2b39801e1 --- /dev/null +++ b/frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here + + +CREATE TABLE grid_view_rev_table ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + object_id TEXT NOT NULL DEFAULT '', + base_rev_id BIGINT NOT NULL DEFAULT 0, + rev_id BIGINT NOT NULL DEFAULT 0, + data BLOB NOT NULL DEFAULT (x''), + state INTEGER NOT NULL DEFAULT 0 +); diff --git a/frontend/rust-lib/flowy-database/src/macros.rs b/frontend/rust-lib/flowy-database/src/macros.rs index e1534bf25f..870938c1d0 100644 --- a/frontend/rust-lib/flowy-database/src/macros.rs +++ b/frontend/rust-lib/flowy-database/src/macros.rs @@ -177,20 +177,20 @@ macro_rules! impl_rev_state_map { } } - impl std::convert::From<$target> for RevisionState { + impl std::convert::From<$target> for crate::disk::RevisionState { fn from(s: $target) -> Self { match s { - $target::Sync => RevisionState::Sync, - $target::Ack => RevisionState::Ack, + $target::Sync => crate::disk::RevisionState::Sync, + $target::Ack => crate::disk::RevisionState::Ack, } } } - impl std::convert::From for $target { - fn from(s: RevisionState) -> Self { + impl std::convert::From for $target { + fn from(s: crate::disk::RevisionState) -> Self { match s { - RevisionState::Sync => $target::Sync, - RevisionState::Ack => $target::Ack, + crate::disk::RevisionState::Sync => $target::Sync, + crate::disk::RevisionState::Ack => $target::Ack, } } } diff --git a/frontend/rust-lib/flowy-database/src/schema.rs b/frontend/rust-lib/flowy-database/src/schema.rs index eda9cd888b..065a13b85f 100644 --- a/frontend/rust-lib/flowy-database/src/schema.rs +++ b/frontend/rust-lib/flowy-database/src/schema.rs @@ -42,6 +42,17 @@ table! { } } +table! { + grid_view_rev_table (id) { + id -> Integer, + object_id -> Text, + base_rev_id -> BigInt, + rev_id -> BigInt, + data -> Binary, + state -> Integer, + } +} + table! { kv_table (key) { key -> Text, @@ -125,6 +136,7 @@ allow_tables_to_appear_in_same_query!( grid_block_index_table, grid_meta_rev_table, grid_rev_table, + grid_view_rev_table, kv_table, rev_snapshot, rev_table, diff --git a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs index af0ae132f1..6f68497edd 100644 --- a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs +++ b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs @@ -4,13 +4,12 @@ use flowy_error::{FlowyError, FlowyResult}; use flowy_revision::{ RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket, }; -use flowy_sync::util::make_delta_from_revisions; +use flowy_sync::util::make_text_delta_from_revisions; use flowy_sync::{ client_folder::{FolderChangeset, FolderPad}, entities::{revision::Revision, ws_data::ServerRevisionWSData}, }; use lib_infra::future::FutureResult; -use lib_ot::core::PhantomAttributes; use parking_lot::RwLock; use std::sync::Arc; @@ -132,7 +131,7 @@ impl FolderEditor { pub struct FolderRevisionCompactor(); impl RevisionCompactor for FolderRevisionCompactor { fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { - let delta = make_delta_from_revisions::(revisions)?; + let delta = make_text_delta_from_revisions(revisions)?; Ok(delta.json_bytes()) } } diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index ba3702038e..709359560b 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -40,6 +40,7 @@ regex = "1.5.6" url = { version = "2"} futures = "0.3.15" atomic_refcell = "0.1.8" +crossbeam-utils = "0.8.7" [dev-dependencies] flowy-test = { path = "../flowy-test" } diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs index e2dac9069d..1ce2b40358 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs @@ -1,4 +1,4 @@ -use crate::entities::RowPB; +use crate::entities::{CreateRowParams, RowPB}; use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; @@ -11,20 +11,17 @@ pub struct CreateBoardCardPayloadPB { #[pb(index = 2)] pub group_id: String, } -pub struct CreateBoardCardParams { - pub grid_id: String, - pub group_id: String, -} -impl TryInto for CreateBoardCardPayloadPB { +impl TryInto for CreateBoardCardPayloadPB { type Error = ErrorCode; - fn try_into(self) -> Result { + fn try_into(self) -> Result { let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?; - Ok(CreateBoardCardParams { + Ok(CreateRowParams { grid_id: grid_id.0, - group_id: group_id.0, + start_row_id: None, + group_id: Some(group_id.0), }) } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs index 47af2e00d3..d42052c747 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs @@ -58,6 +58,7 @@ pub struct CreateRowPayloadPB { pub struct CreateRowParams { pub grid_id: String, pub start_row_id: Option, + pub group_id: Option, } impl TryInto for CreateRowPayloadPB { @@ -68,6 +69,7 @@ impl TryInto for CreateRowPayloadPB { Ok(CreateRowParams { grid_id: grid_id.0, start_row_id: self.start_row_id, + group_id: None, }) } } diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 2c119c55dc..3001ecb52d 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -266,7 +266,7 @@ pub(crate) async fn create_row_handler( ) -> DataResult { let params: CreateRowParams = data.into_inner().try_into()?; let editor = manager.get_grid_editor(params.grid_id.as_ref())?; - let row = editor.create_row(params.start_row_id).await?; + let row = editor.create_row(params).await?; data_result(row) } @@ -419,8 +419,8 @@ pub(crate) async fn create_board_card_handler( data: Data, manager: AppData>, ) -> DataResult { - let params: CreateBoardCardParams = data.into_inner().try_into()?; + let params: CreateRowParams = data.into_inner().try_into()?; let editor = manager.get_grid_editor(params.grid_id.as_ref())?; - let row = editor.create_board_card(¶ms.group_id).await?; + let row = editor.create_row(params).await?; data_result(row) } diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 19a3669c46..4b4ea3b167 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -1,5 +1,6 @@ -use crate::services::block_revision_editor::GridBlockRevisionCompactor; +use crate::services::block_editor::GridBlockRevisionCompactor; use crate::services::grid_editor::{GridRevisionCompactor, GridRevisionEditor}; +use crate::services::grid_view_manager::make_grid_view_rev_manager; use crate::services::persistence::block_index::BlockIndexCache; use crate::services::persistence::kv::GridKVPersistence; use crate::services::persistence::migration::GridMigration; @@ -9,10 +10,10 @@ use bytes::Bytes; use dashmap::DashMap; use flowy_database::ConnectionPool; use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{BuildGridContext, GridRevision}; +use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridViewRevision}; use flowy_revision::disk::{SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionPersistence}; use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence}; -use flowy_sync::client_grid::{make_grid_block_delta, make_grid_delta}; +use flowy_sync::client_grid::{make_grid_block_delta, make_grid_delta, make_grid_view_delta}; use flowy_sync::entities::revision::{RepeatedRevision, Revision}; use std::sync::Arc; use tokio::sync::RwLock; @@ -70,6 +71,15 @@ impl GridManager { 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_all, err)] + async fn create_grid_view>(&self, view_id: T, revisions: RepeatedRevision) -> FlowyResult<()> { + let view_id = view_id.as_ref(); + let rev_manager = make_grid_view_rev_manager(&self.grid_user, view_id).await?; + let _ = rev_manager.reset_object(revisions).await?; Ok(()) } @@ -198,14 +208,23 @@ pub async fn make_grid_view_data( let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?; } - let grid_rev = GridRevision::from_build_context(view_id, build_context); + let grid_id = view_id.to_owned(); + let grid_rev = GridRevision::from_build_context(&grid_id, build_context); // Create grid - let grid_meta_delta = make_grid_delta(&grid_rev); - let grid_delta_data = grid_meta_delta.json_bytes(); + let grid_rev_delta = make_grid_delta(&grid_rev); + let grid_rev_delta_bytes = grid_rev_delta.json_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?; + Revision::initial_revision(user_id, &grid_id, grid_rev_delta_bytes.clone()).into(); + let _ = grid_manager.create_grid(&grid_id, repeated_revision).await?; - Ok(grid_delta_data) + // Create grid view + let grid_view = GridViewRevision::new(view_id.to_owned(), view_id.to_owned()); + let grid_view_delta = make_grid_view_delta(&grid_view); + let grid_view_delta_bytes = grid_view_delta.json_bytes(); + let repeated_revision: RepeatedRevision = + Revision::initial_revision(user_id, view_id, grid_view_delta_bytes).into(); + let _ = grid_manager.create_grid_view(view_id, repeated_revision).await?; + + Ok(grid_rev_delta_bytes) } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs similarity index 97% rename from frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs rename to frontend/rust-lib/flowy-grid/src/services/block_editor.rs index a674098bfc..a53866ee48 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs @@ -5,9 +5,9 @@ use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaCh use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad}; use flowy_sync::entities::revision::Revision; -use flowy_sync::util::make_delta_from_revisions; +use flowy_sync::util::make_text_delta_from_revisions; use lib_infra::future::FutureResult; -use lib_ot::core::PhantomAttributes; + use std::borrow::Cow; use std::sync::Arc; use tokio::sync::RwLock; @@ -200,7 +200,7 @@ impl RevisionObjectBuilder for GridBlockRevisionPadBuilder { pub struct GridBlockRevisionCompactor(); impl RevisionCompactor for GridBlockRevisionCompactor { fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { - let delta = make_delta_from_revisions::(revisions)?; + let delta = make_text_delta_from_revisions(revisions)?; Ok(delta.json_bytes()) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index b2b6ce63fc..457215f8be 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs @@ -1,7 +1,7 @@ use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB}; use crate::manager::GridUser; -use crate::services::block_revision_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor}; +use crate::services::block_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor}; use crate::services::persistence::block_index::BlockIndexCache; use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlockSnapshot}; use dashmap::DashMap; @@ -17,8 +17,6 @@ use std::sync::Arc; type BlockId = String; pub(crate) struct GridBlockManager { - #[allow(dead_code)] - grid_id: String, user: Arc, persistence: Arc, block_editors: DashMap>, @@ -26,16 +24,13 @@ pub(crate) struct GridBlockManager { impl GridBlockManager { pub(crate) async fn new( - grid_id: &str, user: &Arc, block_meta_revs: Vec>, persistence: Arc, ) -> FlowyResult { let block_editors = make_block_editors(user, block_meta_revs).await?; let user = user.clone(); - let grid_id = grid_id.to_owned(); let manager = Self { - grid_id, user, block_editors, persistence, @@ -44,7 +39,7 @@ impl GridBlockManager { } // #[tracing::instrument(level = "trace", skip(self))] - pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult> { + pub(crate) async fn get_block_editor(&self, block_id: &str) -> FlowyResult> { debug_assert!(!block_id.is_empty()); match self.block_editors.get(block_id) { None => { @@ -59,13 +54,13 @@ impl GridBlockManager { async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult> { let block_id = self.persistence.get_block_id(row_id)?; - Ok(self.get_editor(&block_id).await?) + Ok(self.get_block_editor(&block_id).await?) } pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option) -> FlowyResult { let block_id = row_rev.block_id.clone(); let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?; - let editor = self.get_editor(&row_rev.block_id).await?; + let editor = self.get_block_editor(&row_rev.block_id).await?; let mut index_row_order = InsertedRowPB::from(&row_rev); let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?; @@ -82,7 +77,7 @@ impl GridBlockManager { let mut changesets = vec![]; for (block_id, row_revs) in rows_by_block_id { let mut inserted_row_orders = vec![]; - let editor = self.get_editor(&block_id).await?; + let editor = self.get_block_editor(&block_id).await?; let mut row_count = 0; for row in row_revs { let _ = self.persistence.insert(&row.block_id, &row.id)?; @@ -130,7 +125,7 @@ impl GridBlockManager { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { let row_id = row_id.to_owned(); let block_id = self.persistence.get_block_id(&row_id)?; - let editor = self.get_editor(&block_id).await?; + let editor = self.get_block_editor(&block_id).await?; match editor.get_row_info(&row_id).await? { None => {} Some(row_info) => { @@ -147,7 +142,7 @@ impl GridBlockManager { pub(crate) async fn delete_rows(&self, row_orders: Vec) -> FlowyResult> { let mut changesets = vec![]; for grid_block in block_from_row_orders(row_orders) { - let editor = self.get_editor(&grid_block.id).await?; + let editor = self.get_block_editor(&grid_block.id).await?; let row_ids = grid_block .rows .into_iter() @@ -207,7 +202,7 @@ impl GridBlockManager { } pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult> { - let editor = self.get_editor(block_id).await?; + let editor = self.get_block_editor(block_id).await?; editor.get_row_infos::<&str>(None).await } @@ -227,7 +222,7 @@ impl GridBlockManager { } Some(block_ids) => { for block_id in block_ids { - let editor = self.get_editor(&block_id).await?; + let editor = self.get_block_editor(&block_id).await?; let row_revs = editor.get_row_revs::<&str>(None).await?; snapshots.push(GridBlockSnapshot { block_id, row_revs }); } @@ -250,6 +245,7 @@ impl GridBlockManager { } } +/// Initialize each block editor async fn make_block_editors( user: &Arc, block_meta_revs: Vec>, @@ -264,7 +260,7 @@ async fn make_block_editors( } async fn make_block_editor(user: &Arc, block_id: &str) -> FlowyResult { - tracing::trace!("Open block:{} meta editor", block_id); + tracing::trace!("Open block:{} editor", block_id); let token = user.token()?; let user_id = user.user_id()?; let pool = user.db_pool()?; diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs b/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs index 85a53c4d6f..903779c0e8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs @@ -3,7 +3,7 @@ use crate::entities::{ SelectOptionFilterConfigurationPB, TextFilterConfigurationPB, }; use dashmap::DashMap; -use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_grid_data_model::revision::{FieldRevision, FilterConfigurationRevision, RowRevision}; use flowy_sync::client_grid::GridRevisionPad; use std::collections::HashMap; use std::sync::Arc; @@ -74,6 +74,7 @@ impl FilterCache { this } + #[allow(dead_code)] pub(crate) fn remove(&self, filter_id: &FilterId) { let _ = match filter_id.field_type { FieldType::RichText => { @@ -104,13 +105,15 @@ impl FilterCache { /// Refresh the filter according to the field id. pub(crate) async fn refresh_filter_cache( cache: Arc, - field_ids: Option>, + _field_ids: Option>, grid_pad: &Arc>, ) { let grid_pad = grid_pad.read().await; - let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default(); + // let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default(); + // TODO nathan + let filter_revs: Vec> = vec![]; - for filter_rev in filters_revs { + for filter_rev in filter_revs { match grid_pad.get_field_rev(&filter_rev.field_id) { None => {} Some((_, field_rev)) => { diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs index c9850d1c6c..409c5bc5ba 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs @@ -1,3 +1,8 @@ +#![allow(clippy::all)] +#![allow(unused_attributes)] +#![allow(dead_code)] +#![allow(unused_imports)] +#![allow(unused_results)] use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::entities::{FieldType, GridBlockChangesetPB}; use crate::services::block_manager::GridBlockManager; @@ -22,8 +27,10 @@ use std::sync::Arc; use tokio::sync::RwLock; pub(crate) struct GridFilterService { + #[allow(dead_code)] scheduler: Arc, grid_pad: Arc>, + #[allow(dead_code)] block_manager: Arc, filter_cache: Arc, filter_result_cache: Arc, 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 ac5fe92c36..cb35819b55 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -5,14 +5,13 @@ use crate::manager::{GridTaskSchedulerRwLock, GridUser}; use crate::services::block_manager::GridBlockManager; use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes}; use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder}; -use crate::services::filter::{GridFilterChangeset, GridFilterService}; - -use crate::services::group::GridGroupService; +use crate::services::filter::GridFilterService; +use crate::services::grid_view_manager::GridViewManager; use crate::services::persistence::block_index::BlockIndexCache; use crate::services::row::{ make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder, }; -use crate::services::setting::make_grid_setting; + use bytes::Bytes; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::*; @@ -21,26 +20,23 @@ use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeseri use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams}; use flowy_sync::entities::revision::Revision; use flowy_sync::errors::CollaborateResult; -use flowy_sync::util::make_delta_from_revisions; +use flowy_sync::util::make_text_delta_from_revisions; use lib_infra::future::FutureResult; -use lib_ot::core::PhantomAttributes; + use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; pub struct GridRevisionEditor { - pub(crate) grid_id: String, + pub grid_id: String, user: Arc, grid_pad: Arc>, - // view_editor: Arc, + view_manager: Arc, rev_manager: Arc, block_manager: Arc, #[allow(dead_code)] pub(crate) filter_service: Arc, - - #[allow(dead_code)] - pub(crate) group_service: Arc>, } impl Drop for GridRevisionEditor { @@ -63,20 +59,30 @@ impl GridRevisionEditor { let rev_manager = Arc::new(rev_manager); let grid_pad = Arc::new(RwLock::new(grid_pad)); + // Block manager let block_meta_revs = grid_pad.read().await.get_block_meta_revs(); - let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?); + let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence).await?); let filter_service = GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await; - let group_service = - GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await; + + // View manager + let view_manager = Arc::new( + GridViewManager::new( + user.clone(), + grid_pad.clone(), + block_manager.clone(), + Arc::new(task_scheduler.clone()), + ) + .await?, + ); let editor = Arc::new(Self { grid_id: grid_id.to_owned(), user, grid_pad, rev_manager, block_manager, + view_manager, filter_service: Arc::new(filter_service), - group_service: Arc::new(RwLock::new(group_service)), }); Ok(editor) @@ -278,9 +284,15 @@ impl GridRevisionEditor { Ok(()) } - pub async fn create_row(&self, start_row_id: Option) -> FlowyResult { - let row_rev = self.create_row_rev().await?; - self.create_row_pb(row_rev, start_row_id).await + pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult { + let mut row_rev = self.create_row_rev().await?; + + self.view_manager.update_row(&mut row_rev, ¶ms).await; + + let row_pb = self.create_row_pb(row_rev, params.start_row_id.clone()).await?; + + self.view_manager.did_create_row(&row_pb, ¶ms).await; + Ok(row_pb) } pub async fn insert_rows(&self, row_revs: Vec) -> FlowyResult> { @@ -330,7 +342,7 @@ impl GridRevisionEditor { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { let _ = self.block_manager.delete_row(row_id).await?; - self.group_service.read().await.did_delete_card(row_id.to_owned()).await; + self.view_manager.delete_row(row_id).await; Ok(()) } @@ -444,34 +456,15 @@ impl GridRevisionEditor { } pub async fn get_grid_setting(&self) -> FlowyResult { - let read_guard = self.grid_pad.read().await; - let grid_setting_rev = read_guard.get_setting_rev(); - let field_revs = read_guard.get_field_revs(None)?; - let grid_setting = make_grid_setting(grid_setting_rev, &field_revs); - Ok(grid_setting) + self.view_manager.get_setting().await } pub async fn get_grid_filter(&self) -> FlowyResult> { - let read_guard = self.grid_pad.read().await; - match read_guard.get_filters(None) { - Some(filter_revs) => Ok(filter_revs - .iter() - .map(|filter_rev| filter_rev.as_ref().into()) - .collect::>()), - None => Ok(vec![]), - } + self.view_manager.get_filters().await } pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> { - let filter_changeset = GridFilterChangeset::from(¶ms); - let _ = self - .modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?)) - .await?; - - let filter_service = self.filter_service.clone(); - tokio::spawn(async move { - filter_service.apply_changeset(filter_changeset).await; - }); + let _ = self.view_manager.update_setting(params).await?; Ok(()) } @@ -521,22 +514,9 @@ impl GridRevisionEditor { } pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { - match self.block_manager.get_row_rev(row_id).await? { - None => tracing::warn!("Move row failed, can not find the row:{}", row_id), - Some(row_rev) => { - let _ = self - .block_manager - .move_row(row_rev.clone(), from as usize, to as usize) - .await?; - } - } - Ok(()) + self.view_manager.move_row(row_id, from, to).await } - pub async fn move_board_card(&self, group_id: &str, from: i32, to: i32) -> FlowyResult<()> { - self.group_service.write().await.move_card(group_id, from, to).await; - Ok(()) - } pub async fn delta_bytes(&self) -> Bytes { self.grid_pad.read().await.delta_bytes() } @@ -549,7 +529,10 @@ impl GridRevisionEditor { let mut blocks_meta_data = vec![]; if original_blocks.len() == duplicated_blocks.len() { for (index, original_block_meta) in original_blocks.iter().enumerate() { - let grid_block_meta_editor = self.block_manager.get_editor(&original_block_meta.block_id).await?; + let grid_block_meta_editor = self + .block_manager + .get_block_editor(&original_block_meta.block_id) + .await?; let duplicated_block_id = &duplicated_blocks[index].block_id; tracing::trace!("Duplicate block:{} meta data", duplicated_block_id); @@ -568,24 +551,9 @@ impl GridRevisionEditor { }) } - pub async fn create_board_card(&self, group_id: &str) -> FlowyResult { - let mut row_rev = self.create_row_rev().await?; - let _ = self - .group_service - .write() - .await - .update_board_card(&mut row_rev, group_id) - .await; - - let row_pb = self.create_row_pb(row_rev, None).await?; - self.group_service.read().await.did_create_card(group_id, &row_pb).await; - Ok(row_pb) - } - #[tracing::instrument(level = "trace", skip_all, err)] pub async fn load_groups(&self) -> FlowyResult { - let groups = self.group_service.write().await.load_groups().await.unwrap_or_default(); - Ok(RepeatedGridGroupPB { items: groups }) + self.view_manager.load_groups().await } async fn create_row_rev(&self) -> FlowyResult { @@ -716,7 +684,7 @@ impl RevisionCloudService for GridRevisionCloudService { pub struct GridRevisionCompactor(); impl RevisionCompactor for GridRevisionCompactor { fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { - let delta = make_delta_from_revisions::(revisions)?; + let delta = make_text_delta_from_revisions(revisions)?; Ok(delta.json_bytes()) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index e37ca9896a..937d7d725b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -1,31 +1,163 @@ use flowy_error::{FlowyError, FlowyResult}; +use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, RowPB}; +use crate::services::grid_editor_task::GridServiceTaskScheduler; +use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService}; +use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision}; use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder}; -use flowy_sync::client_grid::GridViewRevisionPad; +use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad}; use flowy_sync::entities::revision::Revision; -use lib_infra::future::FutureResult; + +use crate::services::setting::make_grid_setting; +use flowy_sync::entities::grid::GridSettingChangesetParams; +use lib_infra::future::{wrap_future, AFFuture, FutureResult}; use std::sync::Arc; use tokio::sync::RwLock; +pub trait GridViewRevisionDelegate: Send + Sync + 'static { + fn get_field_revs(&self) -> AFFuture>>; + fn get_field_rev(&self, field_id: &str) -> AFFuture>>; +} + +pub trait GridViewRevisionDataSource: Send + Sync + 'static { + fn row_revs(&self) -> AFFuture>>; +} + #[allow(dead_code)] pub struct GridViewRevisionEditor { - #[allow(dead_code)] + user_id: String, + view_id: String, pad: Arc>, - #[allow(dead_code)] rev_manager: Arc, + delegate: Arc, + data_source: Arc, + group_service: Arc>, + scheduler: Arc, } impl GridViewRevisionEditor { - #[allow(dead_code)] - pub async fn new(token: &str, mut rev_manager: RevisionManager) -> FlowyResult { + pub(crate) async fn new( + user_id: &str, + token: &str, + view_id: String, + delegate: Delegate, + data_source: DataSource, + scheduler: Arc, + mut rev_manager: RevisionManager, + ) -> FlowyResult + where + Delegate: GridViewRevisionDelegate, + DataSource: GridViewRevisionDataSource, + { let cloud = Arc::new(GridViewRevisionCloudService { token: token.to_owned(), }); let view_revision_pad = rev_manager.load::(Some(cloud)).await?; let pad = Arc::new(RwLock::new(view_revision_pad)); let rev_manager = Arc::new(rev_manager); + let group_service = GroupService::new(Box::new(pad.clone())).await; + let user_id = user_id.to_owned(); + Ok(Self { + pad, + user_id, + view_id, + rev_manager, + scheduler, + delegate: Arc::new(delegate), + data_source: Arc::new(data_source), + group_service: Arc::new(RwLock::new(group_service)), + }) + } - Ok(Self { pad, rev_manager }) + pub(crate) async fn create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { + match params.group_id.as_ref() { + None => {} + Some(group_id) => { + self.group_service + .read() + .await + .update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id)) + .await; + } + } + todo!() + } + + pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { + match params.group_id.as_ref() { + None => {} + Some(group_id) => { + self.group_service.read().await.did_create_row(group_id, row_pb).await; + } + } + } + + pub(crate) async fn delete_row(&self, row_id: &str) { + self.group_service.read().await.did_delete_card(row_id.to_owned()).await; + } + + pub(crate) async fn load_groups(&self) -> FlowyResult> { + let field_revs = self.delegate.get_field_revs().await; + let row_revs = self.data_source.row_revs().await; + let mut write_guard = self.group_service.write().await; + match write_guard.load_groups(&field_revs, row_revs).await { + None => Ok(vec![]), + Some(groups) => Ok(groups), + } + } + + pub(crate) async fn get_setting(&self) -> GridSettingPB { + let field_revs = self.delegate.get_field_revs().await; + let grid_setting = make_grid_setting(self.pad.read().await.get_setting_rev(), &field_revs); + grid_setting + } + + pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> { + let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await; + Ok(()) + } + + pub(crate) async fn get_filters(&self) -> Vec { + let field_revs = self.delegate.get_field_revs().await; + match self.pad.read().await.get_setting_rev().get_all_filters(&field_revs) { + None => vec![], + Some(filters) => filters + .into_values() + .flatten() + .map(|filter| GridFilterConfiguration::from(filter.as_ref())) + .collect(), + } + } + + async fn modify(&self, f: F) -> FlowyResult<()> + where + F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult>, + { + let mut write_guard = self.pad.write().await; + match f(&mut *write_guard)? { + None => {} + Some(change) => { + let _ = self.apply_change(change).await?; + } + } + Ok(()) + } + + async fn apply_change(&self, change: GridViewRevisionChangeset) -> FlowyResult<()> { + let GridViewRevisionChangeset { delta, md5 } = change; + let user_id = self.user_id.clone(); + let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); + let delta_data = delta.json_bytes(); + let revision = Revision::new( + &self.rev_manager.object_id, + base_rev_id, + rev_id, + delta_data, + &user_id, + md5, + ); + let _ = self.rev_manager.add_local_revision(&revision).await?; + Ok(()) } } @@ -50,3 +182,20 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder { Ok(pad) } } + +impl GroupConfigurationDelegate for Arc> { + fn get_group_configuration(&self, field_rev: Arc) -> AFFuture { + let view_pad = self.clone(); + wrap_future(async move { + let grid_pad = view_pad.read().await; + let configurations = grid_pad.get_groups(&field_rev.id, &field_rev.field_type_rev); + match configurations { + None => default_group_configuration(&field_rev), + Some(mut configurations) => { + assert_eq!(configurations.len(), 1); + (&*configurations.pop().unwrap()).clone() + } + } + }) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs new file mode 100644 index 0000000000..70768ef3a4 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs @@ -0,0 +1,217 @@ +use crate::manager::GridUser; +use crate::services::grid_view_editor::{GridViewRevisionDataSource, GridViewRevisionDelegate, GridViewRevisionEditor}; +use bytes::Bytes; + +use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB}; +use crate::services::grid_editor_task::GridServiceTaskScheduler; + +use crate::services::block_manager::GridBlockManager; +use dashmap::DashMap; +use flowy_error::FlowyResult; +use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_revision::disk::SQLiteGridViewRevisionPersistence; +use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence}; +use flowy_sync::client_grid::GridRevisionPad; +use flowy_sync::entities::revision::Revision; + +use flowy_sync::util::make_text_delta_from_revisions; + +use flowy_sync::entities::grid::GridSettingChangesetParams; + +use lib_infra::future::{wrap_future, AFFuture}; +use std::sync::Arc; +use tokio::sync::RwLock; + +type ViewId = String; + +pub(crate) struct GridViewManager { + user: Arc, + grid_pad: Arc>, + block_manager: Arc, + view_editors: DashMap>, + scheduler: Arc, +} + +impl GridViewManager { + pub(crate) async fn new( + user: Arc, + grid_pad: Arc>, + block_manager: Arc, + scheduler: Arc, + ) -> FlowyResult { + Ok(Self { + user, + grid_pad, + scheduler, + block_manager, + view_editors: DashMap::default(), + }) + } + + pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { + for view_editor in self.view_editors.iter() { + view_editor.create_row(row_rev, params).await; + } + } + + pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { + for view_editor in self.view_editors.iter() { + view_editor.did_create_row(row_pb, params).await; + } + } + + pub(crate) async fn delete_row(&self, row_id: &str) { + for view_editor in self.view_editors.iter() { + view_editor.delete_row(row_id).await; + } + } + + pub(crate) async fn get_setting(&self) -> FlowyResult { + let view_editor = self.get_default_view_editor().await?; + Ok(view_editor.get_setting().await) + } + + pub(crate) async fn update_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> { + let view_editor = self.get_default_view_editor().await?; + let _ = view_editor.update_setting(params).await?; + Ok(()) + } + + pub(crate) async fn get_filters(&self) -> FlowyResult> { + let view_editor = self.get_default_view_editor().await?; + Ok(view_editor.get_filters().await) + } + + pub(crate) async fn load_groups(&self) -> FlowyResult { + let view_editor = self.get_default_view_editor().await?; + let groups = view_editor.load_groups().await?; + Ok(RepeatedGridGroupPB { items: groups }) + } + + pub(crate) async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { + match self.block_manager.get_row_rev(row_id).await? { + None => tracing::warn!("Move row failed, can not find the row:{}", row_id), + Some(row_rev) => { + let _ = self + .block_manager + .move_row(row_rev.clone(), from as usize, to as usize) + .await?; + } + } + Ok(()) + } + + pub(crate) async fn get_view_editor(&self, view_id: &str) -> FlowyResult> { + debug_assert!(!view_id.is_empty()); + match self.view_editors.get(view_id) { + None => { + let editor = Arc::new( + make_view_editor( + &self.user, + view_id, + self.grid_pad.clone(), + self.block_manager.clone(), + self.scheduler.clone(), + ) + .await?, + ); + self.view_editors.insert(view_id.to_owned(), editor.clone()); + Ok(editor) + } + Some(view_editor) => Ok(view_editor.clone()), + } + } + + async fn get_default_view_editor(&self) -> FlowyResult> { + let grid_id = self.grid_pad.read().await.grid_id(); + self.get_view_editor(&grid_id).await + } +} + +async fn make_view_editor( + user: &Arc, + view_id: &str, + delegate: Delegate, + data_source: DataSource, + scheduler: Arc, +) -> FlowyResult +where + Delegate: GridViewRevisionDelegate, + DataSource: GridViewRevisionDataSource, +{ + tracing::trace!("Open view:{} editor", view_id); + + let rev_manager = make_grid_view_rev_manager(user, view_id).await?; + let user_id = user.user_id()?; + let token = user.token()?; + let view_id = view_id.to_owned(); + GridViewRevisionEditor::new(&user_id, &token, view_id, delegate, data_source, scheduler, rev_manager).await +} + +pub async fn make_grid_view_rev_manager(user: &Arc, view_id: &str) -> FlowyResult { + tracing::trace!("Open view:{} editor", view_id); + let user_id = user.user_id()?; + let pool = user.db_pool()?; + + let disk_cache = SQLiteGridViewRevisionPersistence::new(&user_id, pool.clone()); + let rev_persistence = RevisionPersistence::new(&user_id, view_id, disk_cache); + let rev_compactor = GridViewRevisionCompactor(); + + let snapshot_persistence = SQLiteRevisionSnapshotPersistence::new(view_id, pool); + Ok(RevisionManager::new( + &user_id, + view_id, + rev_persistence, + rev_compactor, + snapshot_persistence, + )) +} + +pub struct GridViewRevisionCompactor(); +impl RevisionCompactor for GridViewRevisionCompactor { + fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { + let delta = make_text_delta_from_revisions(revisions)?; + Ok(delta.json_bytes()) + } +} + +impl GridViewRevisionDataSource for Arc { + fn row_revs(&self) -> AFFuture>> { + let block_manager = self.clone(); + + wrap_future(async move { + let blocks = block_manager.get_block_snapshots(None).await.unwrap(); + blocks + .into_iter() + .map(|block| block.row_revs) + .flatten() + .collect::>>() + }) + } +} + +impl GridViewRevisionDelegate for Arc> { + fn get_field_revs(&self) -> AFFuture>> { + let pad = self.clone(); + wrap_future(async move { + match pad.read().await.get_field_revs(None) { + Ok(field_revs) => field_revs, + Err(e) => { + tracing::error!("[GridViewRevisionDelegate] get field revisions failed: {}", e); + vec![] + } + } + }) + } + + fn get_field_rev(&self, field_id: &str) -> AFFuture>> { + let pad = self.clone(); + let field_id = field_id.to_owned(); + wrap_future(async move { + pad.read() + .await + .get_field_rev(&field_id) + .map(|(_, field_rev)| field_rev.clone()) + }) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs index 85c135d0ee..6ea0c78be2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs @@ -4,9 +4,7 @@ use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; use std::sync::Arc; use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK}; -use crate::services::group::{ - Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable, -}; +use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable}; pub type CheckboxGroupController = GroupController; @@ -45,7 +43,6 @@ impl GroupGenerator for CheckboxGroupGenerator { fn generate_groups( _configuration: &Option, _type_option: &Option, - _cell_content_provider: &dyn GroupCellContentProvider, ) -> Vec { let check_group = Group { id: "true".to_string(), diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs index a351913546..0f15538acb 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs @@ -11,14 +11,6 @@ use indexmap::IndexMap; use std::marker::PhantomData; use std::sync::Arc; -pub trait GroupCellContentProvider { - /// We need to group the rows base on the deduplication cell content when the field type is - /// RichText. - fn deduplication_cell_content(&self, _field_id: &str) -> Vec { - vec![] - } -} - pub trait GroupGenerator { type ConfigurationType; type TypeOptionType; @@ -26,7 +18,6 @@ pub trait GroupGenerator { fn generate_groups( configuration: &Option, type_option: &Option, - cell_content_provider: &dyn GroupCellContentProvider, ) -> Vec; } @@ -86,18 +77,14 @@ where T: TypeOptionDataDeserializer, G: GroupGenerator, { - pub fn new( - field_rev: &Arc, - configuration: GroupConfigurationRevision, - cell_content_provider: &dyn GroupCellContentProvider, - ) -> FlowyResult { + pub fn new(field_rev: &Arc, configuration: GroupConfigurationRevision) -> FlowyResult { let configuration = match configuration.content { None => None, Some(content) => Some(C::try_from(Bytes::from(content))?), }; let field_type_rev = field_rev.field_type_rev; let type_option = field_rev.get_type_option_entry::(field_type_rev); - let groups = G::generate_groups(&configuration, &type_option, cell_content_provider); + let groups = G::generate_groups(&configuration, &type_option); let default_group = Group { id: DEFAULT_GROUP_ID.to_owned(), diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs index 50628875a8..933e3f936e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs @@ -8,9 +8,7 @@ use std::sync::Arc; use crate::services::field::{ MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB, }; -use crate::services::group::{ - Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable, -}; +use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable}; // SingleSelect pub type SingleSelectGroupController = GroupController< @@ -59,7 +57,6 @@ impl GroupGenerator for SingleSelectGroupGenerator { fn generate_groups( _configuration: &Option, type_option: &Option, - _cell_content_provider: &dyn GroupCellContentProvider, ) -> Vec { match type_option { None => vec![], @@ -125,7 +122,6 @@ impl GroupGenerator for MultiSelectGroupGenerator { fn generate_groups( _configuration: &Option, type_option: &Option, - _cell_content_provider: &dyn GroupCellContentProvider, ) -> Vec { match type_option { None => vec![], diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs index d03261079a..266abee6d7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs @@ -4,55 +4,42 @@ use crate::entities::{ NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB, }; -use crate::services::block_manager::GridBlockManager; -use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::group::{ - CheckboxGroupController, GroupActionHandler, GroupCellContentProvider, MultiSelectGroupController, - SingleSelectGroupController, + CheckboxGroupController, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController, }; - use bytes::Bytes; use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision}; -use flowy_sync::client_grid::GridRevisionPad; - +use lib_infra::future::AFFuture; +use std::future::Future; use std::sync::Arc; use tokio::sync::RwLock; -pub(crate) struct GridGroupService { - #[allow(dead_code)] - scheduler: Arc, - grid_pad: Arc>, - block_manager: Arc, - group_action_handler: Option>>, +pub trait GroupConfigurationDelegate: Send + Sync + 'static { + fn get_group_configuration(&self, field_rev: Arc) -> AFFuture; } -impl GridGroupService { - pub(crate) async fn new( - grid_pad: Arc>, - block_manager: Arc, - scheduler: S, - ) -> Self { - let scheduler = Arc::new(scheduler); +pub(crate) struct GroupService { + delegate: Box, + action_handler: Option>>, +} + +impl GroupService { + pub(crate) async fn new(delegate: Box) -> Self { Self { - scheduler, - grid_pad, - block_manager, - group_action_handler: None, + delegate, + action_handler: None, } } - pub(crate) async fn load_groups(&mut self) -> Option> { - let field_rev = find_group_field(self.grid_pad.read().await.fields()).unwrap(); + pub(crate) async fn load_groups( + &mut self, + field_revs: &[Arc], + row_revs: Vec>, + ) -> Option> { + let field_rev = find_group_field(field_revs).unwrap(); let field_type: FieldType = field_rev.field_type_rev.into(); - let configuration = self.get_group_configuration(&field_rev).await; - - let blocks = self.block_manager.get_block_snapshots(None).await.unwrap(); - let row_revs = blocks - .into_iter() - .map(|block| block.row_revs) - .flatten() - .collect::>>(); + let configuration = self.delegate.get_group_configuration(field_rev.clone()).await; match self .build_groups(&field_type, &field_rev, row_revs, configuration) @@ -63,36 +50,25 @@ impl GridGroupService { } } - #[tracing::instrument(level = "debug", skip(self, row_rev))] - pub(crate) async fn update_board_card(&self, row_rev: &mut RowRevision, group_id: &str) { - if let Some(group_action_handler) = self.group_action_handler.as_ref() { + pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, group_id: &str, f: F) + where + F: FnOnce(String) -> O, + O: Future>> + Send + Sync + 'static, + { + if let Some(group_action_handler) = self.action_handler.as_ref() { let field_id = group_action_handler.read().await.field_id().to_owned(); - - match self.grid_pad.read().await.get_field_rev(&field_id) { - None => tracing::warn!("Fail to create card because the field does not exist"), - Some((_, field_rev)) => { + match f(field_id).await { + None => {} + Some(field_rev) => { group_action_handler .write() .await - .update_card(row_rev, field_rev, group_id); + .update_card(row_rev, &field_rev, group_id); } } } } - - pub(crate) async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision { - let grid_pad = self.grid_pad.read().await; - let setting = grid_pad.get_setting_rev(); - let configurations = setting.get_groups(&field_rev.id, &field_rev.field_type_rev); - match configurations { - None => default_group_configuration(field_rev), - Some(mut configurations) => { - assert_eq!(configurations.len(), 1); - (&*configurations.pop().unwrap()).clone() - } - } - } - + #[allow(dead_code)] pub async fn move_card(&self, _group_id: &str, _from: i32, _to: i32) { // BoardCardChangesetPB { // group_id: "".to_string(), @@ -103,20 +79,20 @@ impl GridGroupService { // let row_pb = make_row_from_row_rev(row_rev); todo!() } - + #[allow(dead_code)] pub async fn did_delete_card(&self, _row_id: String) { // let changeset = BoardCardChangesetPB::delete(group_id.to_owned(), vec![row_id]); // self.notify_did_update_board(changeset).await; todo!() } - pub async fn did_create_card(&self, group_id: &str, row_pb: &RowPB) { + pub async fn did_create_row(&self, group_id: &str, row_pb: &RowPB) { let changeset = BoardCardChangesetPB::insert(group_id.to_owned(), vec![row_pb.clone()]); self.notify_did_update_board(changeset).await; } pub async fn notify_did_update_board(&self, changeset: BoardCardChangesetPB) { - if self.group_action_handler.is_none() { + if self.action_handler.is_none() { return; } send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard) @@ -143,16 +119,16 @@ impl GridGroupService { // let generator = GroupGenerator::::from_configuration(configuration); } FieldType::SingleSelect => { - let controller = SingleSelectGroupController::new(field_rev, configuration, &self.grid_pad)?; - self.group_action_handler = Some(Arc::new(RwLock::new(controller))); + let controller = SingleSelectGroupController::new(field_rev, configuration)?; + self.action_handler = Some(Arc::new(RwLock::new(controller))); } FieldType::MultiSelect => { - let controller = MultiSelectGroupController::new(field_rev, configuration, &self.grid_pad)?; - self.group_action_handler = Some(Arc::new(RwLock::new(controller))); + let controller = MultiSelectGroupController::new(field_rev, configuration)?; + self.action_handler = Some(Arc::new(RwLock::new(controller))); } FieldType::Checkbox => { - let controller = CheckboxGroupController::new(field_rev, configuration, &self.grid_pad)?; - self.group_action_handler = Some(Arc::new(RwLock::new(controller))); + let controller = CheckboxGroupController::new(field_rev, configuration)?; + self.action_handler = Some(Arc::new(RwLock::new(controller))); } FieldType::URL => { // let generator = GroupGenerator::::from_configuration(configuration); @@ -160,7 +136,7 @@ impl GridGroupService { }; let mut groups = vec![]; - if let Some(group_action_handler) = self.group_action_handler.as_ref() { + if let Some(group_action_handler) = self.action_handler.as_ref() { let mut write_guard = group_action_handler.write().await; let _ = write_guard.group_rows(&row_revs, field_rev)?; groups = write_guard.get_groups(); @@ -182,9 +158,7 @@ fn find_group_field(field_revs: &[Arc]) -> Option> {} - -fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision { +pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision { let field_type: FieldType = field_rev.field_type_rev.into(); let bytes: Bytes = match field_type { FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(), diff --git a/frontend/rust-lib/flowy-grid/src/services/mod.rs b/frontend/rust-lib/flowy-grid/src/services/mod.rs index 2683d1f06a..2addb16686 100644 --- a/frontend/rust-lib/flowy-grid/src/services/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/mod.rs @@ -1,13 +1,14 @@ mod util; +pub mod block_editor; mod block_manager; -pub mod block_revision_editor; pub mod cell; pub mod field; mod filter; pub mod grid_editor; mod grid_editor_task; pub mod grid_view_editor; +pub mod grid_view_manager; pub mod group; pub mod persistence; pub mod row; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index f4e32e1406..2c54f8561e 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow}; use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::grid_editor::GridEditorTest; -use flowy_grid::entities::{FieldType, GridCellIdParams, RowPB}; +use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, RowPB}; use flowy_grid::services::field::*; use flowy_grid_data_model::revision::{ GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, @@ -77,7 +77,12 @@ impl GridRowTest { pub async fn run_script(&mut self, script: RowScript) { match script { RowScript::CreateEmptyRow => { - let row_order = self.editor.create_row(None).await.unwrap(); + let params = CreateRowParams { + grid_id: self.editor.grid_id.clone(), + start_row_id: None, + group_id: None, + }; + let row_order = self.editor.create_row(params).await.unwrap(); self.row_order_by_row_id .insert(row_order.row_id().to_owned(), row_order); self.row_revs = self.get_row_revs().await; diff --git a/frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs b/frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs index 92525be9e0..31b69e0291 100644 --- a/frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs +++ b/frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs @@ -1,5 +1,5 @@ use crate::cache::disk::RevisionDiskCache; -use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; +use crate::disk::{RevisionChangeset, RevisionRecord}; use bytes::Bytes; use diesel::{sql_types::Integer, update, SqliteConnection}; use flowy_database::{ diff --git a/frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs index 52b01440e6..2fcdb7e748 100644 --- a/frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs +++ b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs @@ -1,5 +1,5 @@ use crate::cache::disk::RevisionDiskCache; -use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; +use crate::disk::{RevisionChangeset, RevisionRecord}; use bytes::Bytes; use diesel::{sql_types::Integer, update, SqliteConnection}; use flowy_database::{ @@ -103,7 +103,7 @@ impl GridMetaRevisionSql { record.revision.object_id, record.revision.rev_id ); - let rev_state: GridMetaRevisionState = record.state.into(); + let rev_state: GridBlockRevisionState = record.state.into(); ( dsl::object_id.eq(record.revision.object_id), dsl::base_rev_id.eq(record.revision.base_rev_id), @@ -121,7 +121,7 @@ impl GridMetaRevisionSql { } fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { - let state: GridMetaRevisionState = changeset.state.clone().into(); + let state: GridBlockRevisionState = changeset.state.clone().into(); let filter = dsl::grid_meta_rev_table .filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::object_id.eq(changeset.object_id)); @@ -146,7 +146,7 @@ impl GridMetaRevisionSql { if let Some(rev_ids) = rev_ids { sql = sql.filter(dsl::rev_id.eq_any(rev_ids)); } - let rows = sql.order(dsl::rev_id.asc()).load::(conn)?; + let rows = sql.order(dsl::rev_id.asc()).load::(conn)?; let records = rows .into_iter() .map(|row| mk_revision_record_from_table(user_id, row)) @@ -166,7 +166,7 @@ impl GridMetaRevisionSql { .filter(dsl::rev_id.le(range.end)) .filter(dsl::object_id.eq(object_id)) .order(dsl::rev_id.asc()) - .load::(conn)?; + .load::(conn)?; let revisions = rev_tables .into_iter() @@ -192,31 +192,31 @@ impl GridMetaRevisionSql { #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] #[table_name = "grid_meta_rev_table"] -struct GridMetaRevisionTable { +struct GridBlockRevisionTable { id: i32, object_id: String, base_rev_id: i64, rev_id: i64, data: Vec, - state: GridMetaRevisionState, + state: GridBlockRevisionState, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)] #[repr(i32)] #[sql_type = "Integer"] -pub enum GridMetaRevisionState { +pub enum GridBlockRevisionState { Sync = 0, Ack = 1, } -impl_sql_integer_expression!(GridMetaRevisionState); -impl_rev_state_map!(GridMetaRevisionState); -impl std::default::Default for GridMetaRevisionState { +impl_sql_integer_expression!(GridBlockRevisionState); +impl_rev_state_map!(GridBlockRevisionState); +impl std::default::Default for GridBlockRevisionState { fn default() -> Self { - GridMetaRevisionState::Sync + GridBlockRevisionState::Sync } } -fn mk_revision_record_from_table(user_id: &str, table: GridMetaRevisionTable) -> RevisionRecord { +fn mk_revision_record_from_table(user_id: &str, table: GridBlockRevisionTable) -> RevisionRecord { let md5 = md5(&table.data); let revision = Revision::new( &table.object_id, diff --git a/frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs index 9ddb21bc8c..bb6e561c91 100644 --- a/frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs +++ b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs @@ -1,6 +1,5 @@ use crate::cache::disk::RevisionDiskCache; -use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState}; - +use crate::disk::{RevisionChangeset, RevisionRecord}; use bytes::Bytes; use diesel::{sql_types::Integer, update, SqliteConnection}; use flowy_database::{ diff --git a/frontend/rust-lib/flowy-revision/src/cache/disk/grid_view_impl.rs b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_view_impl.rs new file mode 100644 index 0000000000..2d7cbd59b2 --- /dev/null +++ b/frontend/rust-lib/flowy-revision/src/cache/disk/grid_view_impl.rs @@ -0,0 +1,233 @@ +use crate::disk::{RevisionChangeset, RevisionDiskCache, RevisionRecord}; +use bytes::Bytes; +use diesel::{sql_types::Integer, update, SqliteConnection}; +use flowy_database::{ + impl_sql_integer_expression, insert_or_ignore_into, + prelude::*, + schema::{grid_view_rev_table, grid_view_rev_table::dsl}, + ConnectionPool, +}; +use flowy_error::{internal_error, FlowyError, FlowyResult}; +use flowy_sync::{ + entities::revision::{Revision, RevisionRange}, + util::md5, +}; +use std::sync::Arc; + +pub struct SQLiteGridViewRevisionPersistence { + user_id: String, + pub(crate) pool: Arc, +} + +impl SQLiteGridViewRevisionPersistence { + pub fn new(user_id: &str, pool: Arc) -> Self { + Self { + user_id: user_id.to_owned(), + pool, + } + } +} + +impl RevisionDiskCache for SQLiteGridViewRevisionPersistence { + type Error = FlowyError; + + fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let _ = GridViewRevisionSql::create(revision_records, &*conn)?; + Ok(()) + } + + fn read_revision_records( + &self, + object_id: &str, + rev_ids: Option>, + ) -> Result, Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + let records = GridViewRevisionSql::read(&self.user_id, object_id, rev_ids, &*conn)?; + Ok(records) + } + + fn read_revision_records_with_range( + &self, + object_id: &str, + range: &RevisionRange, + ) -> Result, Self::Error> { + let conn = &*self.pool.get().map_err(internal_error)?; + let revisions = GridViewRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; + Ok(revisions) + } + + fn update_revision_record(&self, changesets: Vec) -> FlowyResult<()> { + let conn = &*self.pool.get().map_err(internal_error)?; + let _ = conn.immediate_transaction::<_, FlowyError, _>(|| { + for changeset in changesets { + let _ = GridViewRevisionSql::update(changeset, conn)?; + } + Ok(()) + })?; + Ok(()) + } + + fn delete_revision_records(&self, object_id: &str, rev_ids: Option>) -> Result<(), Self::Error> { + let conn = &*self.pool.get().map_err(internal_error)?; + let _ = GridViewRevisionSql::delete(object_id, rev_ids, conn)?; + Ok(()) + } + + fn delete_and_insert_records( + &self, + object_id: &str, + deleted_rev_ids: Option>, + inserted_records: Vec, + ) -> Result<(), Self::Error> { + let conn = self.pool.get().map_err(internal_error)?; + conn.immediate_transaction::<_, FlowyError, _>(|| { + let _ = GridViewRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?; + let _ = GridViewRevisionSql::create(inserted_records, &*conn)?; + Ok(()) + }) + } +} + +struct GridViewRevisionSql(); +impl GridViewRevisionSql { + fn create(revision_records: Vec, conn: &SqliteConnection) -> Result<(), FlowyError> { + // Batch insert: https://diesel.rs/guides/all-about-inserts.html + let records = revision_records + .into_iter() + .map(|record| { + tracing::trace!( + "[GridViewRevisionSql] create revision: {}:{:?}", + record.revision.object_id, + record.revision.rev_id + ); + let rev_state: GridViewRevisionState = record.state.into(); + ( + dsl::object_id.eq(record.revision.object_id), + dsl::base_rev_id.eq(record.revision.base_rev_id), + dsl::rev_id.eq(record.revision.rev_id), + dsl::data.eq(record.revision.delta_data), + dsl::state.eq(rev_state), + ) + }) + .collect::>(); + + let _ = insert_or_ignore_into(dsl::grid_view_rev_table) + .values(&records) + .execute(conn)?; + Ok(()) + } + + fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { + let state: GridViewRevisionState = changeset.state.clone().into(); + let filter = dsl::grid_view_rev_table + .filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) + .filter(dsl::object_id.eq(changeset.object_id)); + let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; + tracing::debug!( + "[GridViewRevisionSql] update revision:{} state:to {:?}", + changeset.rev_id, + changeset.state + ); + Ok(()) + } + + fn read( + user_id: &str, + object_id: &str, + rev_ids: Option>, + conn: &SqliteConnection, + ) -> Result, FlowyError> { + let mut sql = dsl::grid_view_rev_table + .filter(dsl::object_id.eq(object_id)) + .into_boxed(); + if let Some(rev_ids) = rev_ids { + sql = sql.filter(dsl::rev_id.eq_any(rev_ids)); + } + let rows = sql.order(dsl::rev_id.asc()).load::(conn)?; + let records = rows + .into_iter() + .map(|row| mk_revision_record_from_table(user_id, row)) + .collect::>(); + + Ok(records) + } + + fn read_with_range( + user_id: &str, + object_id: &str, + range: RevisionRange, + conn: &SqliteConnection, + ) -> Result, FlowyError> { + let rev_tables = dsl::grid_view_rev_table + .filter(dsl::rev_id.ge(range.start)) + .filter(dsl::rev_id.le(range.end)) + .filter(dsl::object_id.eq(object_id)) + .order(dsl::rev_id.asc()) + .load::(conn)?; + + let revisions = rev_tables + .into_iter() + .map(|table| mk_revision_record_from_table(user_id, table)) + .collect::>(); + Ok(revisions) + } + + fn delete(object_id: &str, rev_ids: Option>, conn: &SqliteConnection) -> Result<(), FlowyError> { + let mut sql = diesel::delete(dsl::grid_view_rev_table).into_boxed(); + sql = sql.filter(dsl::object_id.eq(object_id)); + + if let Some(rev_ids) = rev_ids { + tracing::trace!("[GridViewRevisionSql] Delete revision: {}:{:?}", object_id, rev_ids); + sql = sql.filter(dsl::rev_id.eq_any(rev_ids)); + } + + let affected_row = sql.execute(conn)?; + tracing::trace!("[GridViewRevisionSql] Delete {} rows", affected_row); + Ok(()) + } +} + +#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)] +#[table_name = "grid_view_rev_table"] +struct GridViewRevisionTable { + id: i32, + object_id: String, + base_rev_id: i64, + rev_id: i64, + data: Vec, + state: GridViewRevisionState, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)] +#[repr(i32)] +#[sql_type = "Integer"] +pub enum GridViewRevisionState { + Sync = 0, + Ack = 1, +} +impl_sql_integer_expression!(GridViewRevisionState); +impl_rev_state_map!(GridViewRevisionState); + +impl std::default::Default for GridViewRevisionState { + fn default() -> Self { + GridViewRevisionState::Sync + } +} + +fn mk_revision_record_from_table(user_id: &str, table: GridViewRevisionTable) -> RevisionRecord { + let md5 = md5(&table.data); + let revision = Revision::new( + &table.object_id, + table.base_rev_id, + table.rev_id, + Bytes::from(table.data), + user_id, + md5, + ); + RevisionRecord { + revision, + state: table.state.into(), + write_to_disk: false, + } +} diff --git a/frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs b/frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs index 14614523ce..94c76fab11 100644 --- a/frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs +++ b/frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs @@ -1,10 +1,12 @@ mod document_impl; mod grid_block_impl; mod grid_impl; +mod grid_view_impl; pub use document_impl::*; pub use grid_block_impl::*; pub use grid_impl::*; +pub use grid_view_impl::*; use flowy_error::FlowyResult; use flowy_sync::entities::revision::{RevId, Revision, RevisionRange}; diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs index 691dd5e185..31e6265757 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs @@ -1,4 +1,4 @@ -use crate::revision::{GridBlockRevision, SettingRevision}; +use crate::revision::GridBlockRevision; use bytes::Bytes; use indexmap::IndexMap; use nanoid::nanoid; @@ -23,9 +23,6 @@ pub struct GridRevision { pub grid_id: String, pub fields: Vec>, pub blocks: Vec>, - - #[serde(default)] - pub setting: SettingRevision, } impl GridRevision { @@ -34,7 +31,6 @@ impl GridRevision { grid_id: grid_id.to_owned(), fields: vec![], blocks: vec![], - setting: SettingRevision::default(), } } @@ -43,7 +39,6 @@ impl GridRevision { grid_id: grid_id.to_owned(), fields: context.field_revs, blocks: context.blocks.into_iter().map(Arc::new).collect(), - setting: Default::default(), } } } diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs index 2b8861ba49..2fcc2fc4eb 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs @@ -2,6 +2,7 @@ use crate::revision::SettingRevision; use nanoid::nanoid; use serde::{Deserialize, Serialize}; +#[allow(dead_code)] pub fn gen_grid_view_id() -> String { nanoid!(6) } @@ -21,9 +22,9 @@ pub struct GridViewRevision { } impl GridViewRevision { - pub fn new(grid_id: String) -> Self { + pub fn new(grid_id: String, view_id: String) -> Self { GridViewRevision { - view_id: gen_grid_view_id(), + view_id, grid_id, setting: Default::default(), row_orders: vec![], diff --git a/shared-lib/flowy-grid-data-model/tests/serde_test.rs b/shared-lib/flowy-grid-data-model/tests/serde_test.rs index 8a630a4651..b544e10588 100644 --- a/shared-lib/flowy-grid-data-model/tests/serde_test.rs +++ b/shared-lib/flowy-grid-data-model/tests/serde_test.rs @@ -6,8 +6,5 @@ fn grid_default_serde_test() { let grid = GridRevision::new(&grid_id); let json = serde_json::to_string(&grid).unwrap(); - assert_eq!( - json, - r#"{"grid_id":"1","fields":[],"blocks":[],"setting":{"layout":0,"filters":[],"groups":[]}}"# - ) + assert_eq!(json, r#"{"grid_id":"1","fields":[],"blocks":[]}"#) } diff --git a/shared-lib/flowy-sync/src/client_folder/builder.rs b/shared-lib/flowy-sync/src/client_folder/builder.rs index 75a1343570..6581591c25 100644 --- a/shared-lib/flowy-sync/src/client_folder/builder.rs +++ b/shared-lib/flowy-sync/src/client_folder/builder.rs @@ -1,5 +1,5 @@ use crate::entities::folder::FolderDelta; -use crate::util::make_delta_from_revisions; +use crate::util::make_text_delta_from_revisions; use crate::{ client_folder::{default_folder_delta, FolderPad}, entities::revision::Revision, @@ -7,7 +7,7 @@ use crate::{ }; use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision}; -use lib_ot::core::PhantomAttributes; + use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -37,7 +37,7 @@ impl FolderPadBuilder { } pub(crate) fn build_with_revisions(self, revisions: Vec) -> CollaborateResult { - let mut folder_delta: FolderDelta = make_delta_from_revisions::(revisions)?; + let mut folder_delta: FolderDelta = make_text_delta_from_revisions(revisions)?; if folder_delta.is_empty() { folder_delta = default_folder_delta(); } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs index 25a7de66a8..9b471a206b 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs @@ -1,14 +1,11 @@ -use crate::entities::grid::{ - CreateGridFilterParams, CreateGridGroupParams, FieldChangesetParams, GridSettingChangesetParams, -}; +use crate::entities::grid::FieldChangesetParams; use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; -use crate::util::{cal_diff, make_delta_from_revisions}; +use crate::util::{cal_diff, make_text_delta_from_revisions}; use bytes::Bytes; use flowy_grid_data_model::revision::{ - gen_block_id, gen_grid_filter_id, gen_grid_group_id, gen_grid_id, FieldRevision, FieldTypeRevision, - FilterConfigurationRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, GridRevision, - GroupConfigurationRevision, SettingRevision, + gen_block_id, gen_grid_id, FieldRevision, FieldTypeRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, + GridRevision, }; use lib_infra::util::move_vec_element; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder}; @@ -68,7 +65,7 @@ impl GridRevisionPad { } pub fn from_revisions(revisions: Vec) -> CollaborateResult { - let grid_delta: GridRevisionDelta = make_delta_from_revisions::(revisions)?; + let grid_delta: GridRevisionDelta = make_text_delta_from_revisions(revisions)?; Self::from_delta(grid_delta) } @@ -347,95 +344,6 @@ impl GridRevisionPad { }) } - pub fn get_setting_rev(&self) -> &SettingRevision { - &self.grid_rev.setting - } - - /// If layout is None, then the default layout will be the read from GridSettingRevision - pub fn get_filters(&self, field_ids: Option>) -> Option>> { - let mut filter_revs = vec![]; - let field_revs = self.get_field_revs(None).ok()?; - - field_revs.iter().for_each(|field_rev| { - let mut is_contain = true; - if let Some(field_ids) = &field_ids { - is_contain = field_ids.contains(&field_rev.id); - } - - if is_contain { - // Only return the filters for the current fields' type. - let field_id = &field_rev.id; - let field_type_rev = &field_rev.field_type_rev; - if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(field_id, field_type_rev) { - filter_revs.append(&mut t_filter_revs); - } - } - }); - - Some(filter_revs) - } - - pub fn update_grid_setting_rev( - &mut self, - changeset: GridSettingChangesetParams, - ) -> CollaborateResult> { - self.modify_grid(|grid_rev| { - let mut is_changed = None; - if let Some(params) = changeset.insert_filter { - grid_rev - .setting - .insert_filter(¶ms.field_id, ¶ms.field_type_rev, make_filter_revision(¶ms)); - is_changed = Some(()) - } - if let Some(params) = changeset.delete_filter { - if let Some(filters) = grid_rev - .setting - .get_mut_filters(¶ms.field_id, ¶ms.field_type_rev) - { - filters.retain(|filter| filter.id != params.filter_id); - } - } - if let Some(params) = changeset.insert_group { - grid_rev - .setting - .insert_group(¶ms.field_id, ¶ms.field_type_rev, make_group_revision(¶ms)); - is_changed = Some(()); - } - if let Some(params) = changeset.delete_group { - if let Some(groups) = grid_rev - .setting - .get_mut_groups(¶ms.field_id, ¶ms.field_type_rev) - { - groups.retain(|filter| filter.id != params.group_id); - } - } - if let Some(_sort) = changeset.insert_sort { - // let rev = GridSortRevision { - // id: gen_grid_sort_id(), - // field_id: sort.field_id, - // }; - // - // grid_rev - // .setting - // .sorts - // .entry(layout_rev.clone()) - // .or_insert_with(std::vec::Vec::new) - // .push(rev); - is_changed = Some(()) - } - - if let Some(_delete_sort_id) = changeset.delete_sort { - // match grid_rev.setting.sorts.get_mut(&layout_rev) { - // Some(sorts) => sorts.retain(|sort| sort.id != delete_sort_id), - // None => { - // tracing::warn!("Can't find the sort with {:?}", layout_rev); - // } - // } - } - Ok(is_changed) - }) - } - pub fn md5(&self) -> String { md5(&self.delta.json_bytes()) } @@ -548,21 +456,3 @@ impl std::default::Default for GridRevisionPad { } } } - -fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision { - FilterConfigurationRevision { - id: gen_grid_filter_id(), - field_id: params.field_id.clone(), - condition: params.condition, - content: params.content.clone(), - } -} - -fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision { - GroupConfigurationRevision { - id: gen_grid_group_id(), - field_id: params.field_id.clone(), - field_type_rev: params.field_type_rev, - content: params.content.clone(), - } -} diff --git a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs index a3906a4aaf..dd6cc6f977 100644 --- a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs @@ -1,9 +1,11 @@ +use crate::entities::grid::{CreateGridFilterParams, CreateGridGroupParams, GridSettingChangesetParams}; use crate::entities::revision::{md5, Revision}; use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_text_delta_from_revisions}; use flowy_grid_data_model::revision::{ - FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision, - GroupConfigurationRevision, GroupConfigurationsByFieldId, SortConfigurationsByFieldId, + gen_grid_filter_id, gen_grid_group_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, + FilterConfigurationsByFieldId, GridViewRevision, GroupConfigurationRevision, GroupConfigurationsByFieldId, + SettingRevision, SortConfigurationsByFieldId, }; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder}; use std::sync::Arc; @@ -23,8 +25,8 @@ impl std::ops::Deref for GridViewRevisionPad { } impl GridViewRevisionPad { - pub fn new(grid_id: String) -> Self { - let view = Arc::new(GridViewRevision::new(grid_id)); + pub fn new(grid_id: String, view_id: String) -> Self { + let view = Arc::new(GridViewRevision::new(grid_id, view_id)); let json = serde_json::to_string(&view).unwrap(); let delta = TextDeltaBuilder::new().insert(&json).build(); Self { view, delta } @@ -48,6 +50,59 @@ impl GridViewRevisionPad { Self::from_delta(delta) } + pub fn get_setting_rev(&self) -> &SettingRevision { + &self.view.setting + } + + pub fn update_setting( + &mut self, + changeset: GridSettingChangesetParams, + ) -> CollaborateResult> { + self.modify(|view| { + let mut is_changed = None; + if let Some(params) = changeset.insert_filter { + view.setting.filters.insert_object( + ¶ms.field_id, + ¶ms.field_type_rev, + make_filter_revision(¶ms), + ); + is_changed = Some(()) + } + if let Some(params) = changeset.delete_filter { + if let Some(filters) = view + .setting + .filters + .get_mut_objects(¶ms.field_id, ¶ms.field_type_rev) + { + filters.retain(|filter| filter.id != params.filter_id); + is_changed = Some(()) + } + } + if let Some(params) = changeset.insert_group { + view.setting.groups.remove_all(); + view.setting.groups.insert_object( + ¶ms.field_id, + ¶ms.field_type_rev, + make_group_revision(¶ms), + ); + + is_changed = Some(()); + } + if let Some(params) = changeset.delete_group { + if let Some(groups) = view + .setting + .groups + .get_mut_objects(¶ms.field_id, ¶ms.field_type_rev) + { + groups.retain(|group| group.id != params.group_id); + is_changed = Some(()); + } + } + + Ok(is_changed) + }) + } + pub fn get_all_groups(&self, field_revs: &[Arc]) -> Option { self.setting.groups.get_all_objects(field_revs) } @@ -161,6 +216,24 @@ impl GridViewRevisionPad { } } +fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision { + FilterConfigurationRevision { + id: gen_grid_filter_id(), + field_id: params.field_id.clone(), + condition: params.condition, + content: params.content.clone(), + } +} + +fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision { + GroupConfigurationRevision { + id: gen_grid_group_id(), + field_id: params.field_id.clone(), + field_type_rev: params.field_type_rev, + content: params.content.clone(), + } +} + pub struct GridViewRevisionChangeset { pub delta: TextDelta, pub md5: String, @@ -171,3 +244,8 @@ pub fn make_grid_view_rev_json_str(grid_revision: &GridViewRevision) -> Collabor .map_err(|err| internal_error(format!("Serialize grid view to json str failed. {:?}", err)))?; Ok(json) } + +pub fn make_grid_view_delta(grid_view: &GridViewRevision) -> TextDelta { + let json = serde_json::to_string(grid_view).unwrap(); + TextDeltaBuilder::new().insert(&json).build() +} diff --git a/shared-lib/lib-infra/src/future.rs b/shared-lib/lib-infra/src/future.rs index 9077dd18b7..a6bad3b298 100644 --- a/shared-lib/lib-infra/src/future.rs +++ b/shared-lib/lib-infra/src/future.rs @@ -8,20 +8,20 @@ use std::{ task::{Context, Poll}, }; -pub fn wrap_future(f: T) -> FnFuture +pub fn wrap_future(f: T) -> AFFuture where T: Future + Send + Sync + 'static, { - FnFuture { fut: Box::pin(f) } + AFFuture { fut: Box::pin(f) } } #[pin_project] -pub struct FnFuture { +pub struct AFFuture { #[pin] pub fut: Pin + Sync + Send>>, } -impl Future for FnFuture +impl Future for AFFuture where T: Send + Sync, {