From e28e5a0649ebee0f2d816f9373aef423bdd2ebe7 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Sat, 28 Oct 2023 11:48:28 +0800 Subject: [PATCH] refactor: rename group (#3815) * chore: add group operation interceptor * refactor: impl interceptor trait * chore: update type option when group change * test: fix test --- frontend/appflowy_tauri/src-tauri/Cargo.lock | 21 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 16 +- frontend/rust-lib/Cargo.lock | 21 +- frontend/rust-lib/Cargo.toml | 16 +- .../tests/database/local_test/test.rs | 2 +- .../flowy-database2/src/event_handler.rs | 22 +- .../src/services/database/database_editor.rs | 244 ++++++++++-------- .../src/services/database_view/mod.rs | 2 + .../src/services/database_view/view_editor.rs | 217 +++++----------- .../src/services/database_view/view_filter.rs | 8 +- .../src/services/database_view/view_group.rs | 48 +++- .../services/database_view/view_operation.rs | 126 +++++++++ .../src/services/database_view/view_sort.rs | 8 +- .../src/services/database_view/views.rs | 66 ++--- .../selection_type_option/select_ids.rs | 1 - .../field/type_options/type_option.rs | 10 +- .../field/type_options/type_option_cell.rs | 9 +- .../src/services/filter/controller.rs | 2 +- .../src/services/group/action.rs | 40 ++- .../src/services/group/configuration.rs | 19 +- .../src/services/group/controller.rs | 155 +++++------ .../controller_impls/checkbox_controller.rs | 63 +++-- .../group/controller_impls/date_controller.rs | 128 ++++----- .../controller_impls/default_controller.rs | 13 +- .../multi_select_controller.rs | 114 ++++---- .../single_select_controller.rs | 115 +++++---- .../select_option_controller/util.rs | 11 +- .../group/controller_impls/url_controller.rs | 81 ++++-- .../src/services/group/entities.rs | 8 +- .../src/services/group/group_builder.rs | 102 +++++++- .../src/services/sort/controller.rs | 2 +- .../tests/database/group_test/script.rs | 26 -- frontend/rust-lib/flowy-error/Cargo.toml | 1 + frontend/rust-lib/flowy-error/src/errors.rs | 6 + 34 files changed, 980 insertions(+), 743 deletions(-) create mode 100644 frontend/rust-lib/flowy-database2/src/services/database_view/view_operation.rs diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index c43f9d217c..545b6438e4 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -454,7 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -854,7 +854,7 @@ dependencies = [ [[package]] name = "collab" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -873,7 +873,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -903,7 +903,7 @@ dependencies = [ [[package]] name = "collab-derive" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "proc-macro2", "quote", @@ -915,7 +915,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "collab", @@ -935,7 +935,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "bytes", @@ -949,7 +949,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "chrono", @@ -991,7 +991,7 @@ dependencies = [ [[package]] name = "collab-persistence" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "async-trait", "bincode", @@ -1012,7 +1012,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "collab", @@ -2115,6 +2115,7 @@ dependencies = [ "serde_json", "serde_repr", "thiserror", + "tokio", "tokio-postgres", "url", "validator", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 3ee84542e5..980d4cb093 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -48,14 +48,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } -collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" } +collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 041ccd56c1..e94ab9c832 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -461,7 +461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -721,7 +721,7 @@ dependencies = [ [[package]] name = "collab" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -740,7 +740,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -770,7 +770,7 @@ dependencies = [ [[package]] name = "collab-derive" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "proc-macro2", "quote", @@ -782,7 +782,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "collab", @@ -802,7 +802,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "bytes", @@ -816,7 +816,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "chrono", @@ -858,7 +858,7 @@ dependencies = [ [[package]] name = "collab-persistence" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "async-trait", "bincode", @@ -879,7 +879,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "async-trait", @@ -906,7 +906,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=ff10abd5e#ff10abd5e41b9a7d118cbb79f8c2bf9ac27c0ded" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8861d7a#8861d7a45a2bda493f307483561d92e31fffff4c" dependencies = [ "anyhow", "collab", @@ -1938,6 +1938,7 @@ dependencies = [ "serde_json", "serde_repr", "thiserror", + "tokio", "tokio-postgres", "url", "validator", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 1aaf419f15..1c81d09ff4 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -92,11 +92,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } -collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" } +collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } +collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" } diff --git a/frontend/rust-lib/event-integration/tests/database/local_test/test.rs b/frontend/rust-lib/event-integration/tests/database/local_test/test.rs index 3b3cdd171e..5ed18809fc 100644 --- a/frontend/rust-lib/event-integration/tests/database/local_test/test.rs +++ b/frontend/rust-lib/event-integration/tests/database/local_test/test.rs @@ -761,7 +761,7 @@ async fn rename_group_event_test() { } #[tokio::test] -async fn hide_group_event_test2() { +async fn hide_group_event_test() { let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index b72e2e73c5..3103111812 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, Weak}; use collab_database::database::gen_row_id; use collab_database::rows::RowId; +use tokio::sync::oneshot; use flowy_error::{FlowyError, FlowyResult}; use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult}; @@ -15,7 +16,7 @@ use crate::services::field::{ type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset, }; use crate::services::field_settings::FieldSettingsChangesetParams; -use crate::services::group::{GroupChangeset, GroupChangesets}; +use crate::services::group::GroupChangeset; use crate::services::share::csv::CSVFormat; fn upgrade_manager( @@ -725,18 +726,15 @@ pub(crate) async fn update_group_handler( let view_id = params.view_id.clone(); let database_editor = manager.get_database_with_view_id(&view_id).await?; let group_changeset = GroupChangeset::from(params); - database_editor - .update_group(&view_id, group_changeset.clone()) - .await?; - database_editor - .update_group_setting( - &view_id, - GroupChangesets { - update_groups: vec![group_changeset], - }, - ) - .await?; + let (tx, rx) = oneshot::channel(); + tokio::spawn(async move { + let result = database_editor + .update_group(&view_id, vec![group_changeset].into()) + .await; + let _ = tx.send(result); + }); + let _ = rx.await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 6bafa9361a..b51252a54d 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -8,10 +8,11 @@ use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowDetai use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting}; use futures::StreamExt; use tokio::sync::{broadcast, RwLock}; +use tracing::{event, warn}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_task::TaskDispatcher; -use lib_infra::future::{to_fut, Fut}; +use lib_infra::future::{to_fut, Fut, FutureResult}; use crate::entities::*; use crate::notification::{send_notification, DatabaseNotification}; @@ -20,7 +21,9 @@ use crate::services::cell::{ }; use crate::services::database::util::database_view_setting_pb_from_view; use crate::services::database::UpdatedRow; -use crate::services::database_view::{DatabaseViewChanged, DatabaseViewData, DatabaseViews}; +use crate::services::database_view::{ + DatabaseViewChanged, DatabaseViewEditor, DatabaseViewOperation, DatabaseViews, EditorByViewId, +}; use crate::services::field::checklist_type_option::ChecklistCellChangeset; use crate::services::field::{ default_type_option_data_from_type, select_type_option_from_field, transform_type_option, @@ -32,8 +35,7 @@ use crate::services::field_settings::{ }; use crate::services::filter::Filter; use crate::services::group::{ - default_group_setting, GroupChangeset, GroupChangesets, GroupSetting, GroupSettingChangeset, - RowChangeset, + default_group_setting, GroupChangesets, GroupSetting, GroupSettingChangeset, RowChangeset, }; use crate::services::share::csv::{CSVExport, CSVFormat}; use crate::services::sort::Sort; @@ -51,12 +53,6 @@ impl DatabaseEditor { task_scheduler: Arc>, ) -> FlowyResult { let cell_cache = AnyTypeCache::::new(); - let database_view_data = Arc::new(DatabaseViewDataImpl { - database: database.clone(), - task_scheduler: task_scheduler.clone(), - cell_cache: cell_cache.clone(), - }); - let database_id = database.lock().get_database_id(); // Receive database sync state and send to frontend via the notification @@ -93,8 +89,24 @@ impl DatabaseEditor { } }); - let database_views = - Arc::new(DatabaseViews::new(database.clone(), cell_cache.clone(), database_view_data).await?); + // Used to cache the view of the database for fast access. + let editor_by_view_id = Arc::new(RwLock::new(EditorByViewId::default())); + let view_operation = Arc::new(DatabaseViewOperationImpl { + database: database.clone(), + task_scheduler: task_scheduler.clone(), + cell_cache: cell_cache.clone(), + editor_by_view_id: editor_by_view_id.clone(), + }); + + let database_views = Arc::new( + DatabaseViews::new( + database.clone(), + cell_cache.clone(), + view_operation, + editor_by_view_id, + ) + .await?, + ); Ok(Self { database, cell_cache, @@ -177,40 +189,9 @@ impl DatabaseEditor { Ok(self.database.lock().delete_view(view_id)) } - pub async fn update_group_setting( - &self, - view_id: &str, - group_setting_changeset: GroupChangesets, - ) -> FlowyResult<()> { + pub async fn update_group(&self, view_id: &str, changesets: GroupChangesets) -> FlowyResult<()> { let view_editor = self.database_views.get_view_editor(view_id).await?; - view_editor - .v_update_group_setting(group_setting_changeset) - .await?; - Ok(()) - } - - pub async fn update_group( - &self, - view_id: &str, - group_changeset: GroupChangeset, - ) -> FlowyResult<()> { - let view_editor = self.database_views.get_view_editor(view_id).await?; - let type_option = view_editor.update_group(group_changeset.clone()).await?; - - if let Some(type_option_data) = type_option { - let field = self.get_field(&group_changeset.field_id); - if field.is_some() { - let _ = self - .update_field_type_option( - view_id, - &group_changeset.field_id, - type_option_data, - field.unwrap(), - ) - .await; - } - } - + view_editor.v_update_group(changesets).await?; Ok(()) } @@ -295,9 +276,7 @@ impl DatabaseEditor { .set_width_at_if_not_none(params.width.map(|value| value as i64)) .set_visibility_if_not_none(params.visibility); }); - self - .notify_did_update_database_field(¶ms.field_id) - .await?; + notify_did_update_database_field(&self.database, ¶ms.field_id)?; Ok(()) } @@ -331,30 +310,13 @@ impl DatabaseEditor { pub async fn update_field_type_option( &self, view_id: &str, - field_id: &str, + _field_id: &str, type_option_data: TypeOptionData, old_field: Field, ) -> FlowyResult<()> { - let field_type = FieldType::from(old_field.field_type); - self - .database - .lock() - .fields - .update_field(field_id, |update| { - if old_field.is_primary { - tracing::warn!("Cannot update primary field type"); - } else { - update.update_type_options(|type_options_update| { - type_options_update.insert(&field_type.to_string(), type_option_data); - }); - } - }); + let view_editor = self.database_views.get_view_editor(view_id).await?; + update_field_type_option_fn(&self.database, &view_editor, type_option_data, old_field).await?; - self - .database_views - .did_update_field_type_option(view_id, field_id, &old_field) - .await?; - let _ = self.notify_did_update_database_field(field_id).await; Ok(()) } @@ -398,7 +360,7 @@ impl DatabaseEditor { }, } - self.notify_did_update_database_field(field_id).await?; + notify_did_update_database_field(&self.database, field_id)?; Ok(()) } @@ -435,7 +397,7 @@ impl DatabaseEditor { let params = self.database.lock().duplicate_row(row_id); match params { None => { - tracing::warn!("Failed to duplicate row: {}", row_id); + warn!("Failed to duplicate row: {}", row_id); }, Some(params) => { let _ = self.create_row(view_id, group_id, params).await; @@ -585,7 +547,7 @@ impl DatabaseEditor { cover: row_meta.cover_url, }) } else { - tracing::warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id); + warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id); None } } @@ -594,7 +556,7 @@ impl DatabaseEditor { if self.database.lock().views.is_row_exist(view_id, row_id) { self.database.lock().get_row_detail(row_id) } else { - tracing::warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id); + warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id); None } } @@ -984,7 +946,7 @@ impl DatabaseEditor { let row_detail = self.get_row_detail(view_id, &from_row); match row_detail { None => { - tracing::warn!( + warn!( "Move row between group failed, can not find the row:{}", from_row ) @@ -1054,7 +1016,7 @@ impl DatabaseEditor { match self.database_views.get_view_editor(view_id).await { Ok(view) => view.v_get_all_calendar_events().await.unwrap_or_default(), Err(_) => { - tracing::warn!("Can not find the view: {}", view_id); + warn!("Can not find the view: {}", view_id); vec![] }, } @@ -1066,7 +1028,7 @@ impl DatabaseEditor { view_id: &str, ) -> FlowyResult> { let _database_view = self.database_views.get_view_editor(view_id).await?; - todo!() + Ok(vec![]) } #[tracing::instrument(level = "trace", skip_all)] @@ -1084,28 +1046,6 @@ impl DatabaseEditor { Ok(()) } - #[tracing::instrument(level = "trace", skip_all, err)] - async fn notify_did_update_database_field(&self, field_id: &str) -> FlowyResult<()> { - let (database_id, field) = { - let database = self.database.lock(); - let database_id = database.get_database_id(); - let field = database.fields.get_field(field_id); - (database_id, field) - }; - - if let Some(field) = field { - let updated_field = FieldPB::from(field); - let notified_changeset = - DatabaseFieldChangesetPB::update(&database_id, vec![updated_field.clone()]); - self.notify_did_update_database(notified_changeset).await?; - send_notification(field_id, DatabaseNotification::DidUpdateField) - .payload(updated_field) - .send(); - } - - Ok(()) - } - async fn notify_did_update_database( &self, changeset: DatabaseFieldChangesetPB, @@ -1134,7 +1074,7 @@ impl DatabaseEditor { pub async fn get_database_data(&self, view_id: &str) -> FlowyResult { let database_view = self.database_views.get_view_editor(view_id).await?; let view = database_view - .get_view() + .v_get_view() .await .ok_or_else(FlowyError::record_not_found)?; let rows = database_view.v_get_rows().await; @@ -1284,13 +1224,14 @@ fn cell_changesets_from_cell_by_field_id( .collect() } -struct DatabaseViewDataImpl { +struct DatabaseViewOperationImpl { database: Arc, task_scheduler: Arc>, cell_cache: CellCache, + editor_by_view_id: Arc>, } -impl DatabaseViewData for DatabaseViewDataImpl { +impl DatabaseViewOperation for DatabaseViewOperationImpl { fn get_database(&self) -> Arc { self.database.clone() } @@ -1305,14 +1246,8 @@ impl DatabaseViewData for DatabaseViewDataImpl { to_fut(async move { fields.into_iter().map(Arc::new).collect() }) } - fn get_field(&self, field_id: &str) -> Fut>> { - let field = self - .database - .lock() - .fields - .get_field(field_id) - .map(Arc::new); - to_fut(async move { field }) + fn get_field(&self, field_id: &str) -> Option { + self.database.lock().fields.get_field(field_id) } fn create_field( @@ -1336,6 +1271,29 @@ impl DatabaseViewData for DatabaseViewDataImpl { to_fut(async move { field }) } + fn update_field( + &self, + view_id: &str, + type_option_data: TypeOptionData, + old_field: Field, + ) -> FutureResult<(), FlowyError> { + let view_id = view_id.to_string(); + let weak_editor_by_view_id = Arc::downgrade(&self.editor_by_view_id); + let weak_database = Arc::downgrade(&self.database); + FutureResult::new(async move { + if let (Some(database), Some(editor_by_view_id)) = + (weak_database.upgrade(), weak_editor_by_view_id.upgrade()) + { + let view_editor = editor_by_view_id.read().await.get(&view_id).cloned(); + if let Some(view_editor) = view_editor { + let _ = + update_field_type_option_fn(&database, &view_editor, type_option_data, old_field).await; + } + } + Ok(()) + }) + } + fn get_primary_field(&self) -> Fut>> { let field = self .database @@ -1559,3 +1517,73 @@ impl DatabaseViewData for DatabaseViewDataImpl { .send() } } + +#[tracing::instrument(level = "trace", skip_all, err)] +pub async fn update_field_type_option_fn( + database: &Arc, + view_editor: &Arc, + type_option_data: TypeOptionData, + old_field: Field, +) -> FlowyResult<()> { + if type_option_data.is_empty() { + warn!("Update type option with empty data"); + return Ok(()); + } + let field_type = FieldType::from(old_field.field_type); + database + .lock() + .fields + .update_field(&old_field.id, |update| { + if old_field.is_primary { + warn!("Cannot update primary field type"); + } else { + update.update_type_options(|type_options_update| { + event!( + tracing::Level::TRACE, + "insert type option to field type: {:?}", + field_type + ); + type_options_update.insert(&field_type.to_string(), type_option_data); + }); + } + }); + + let _ = notify_did_update_database_field(database, &old_field.id); + view_editor + .v_did_update_field_type_option(&old_field) + .await?; + Ok(()) +} + +#[tracing::instrument(level = "trace", skip_all, err)] +fn notify_did_update_database_field( + database: &Arc, + field_id: &str, +) -> FlowyResult<()> { + let (database_id, field, views) = { + let database = database + .try_lock() + .ok_or(FlowyError::internal().with_context("fail to acquire the lock of database"))?; + let database_id = database.get_database_id(); + let field = database.fields.get_field(field_id); + let views = database.get_all_views_description(); + (database_id, field, views) + }; + + if let Some(field) = field { + let updated_field = FieldPB::from(field); + let notified_changeset = + DatabaseFieldChangesetPB::update(&database_id, vec![updated_field.clone()]); + + for view in views { + send_notification(&view.id, DatabaseNotification::DidUpdateFields) + .payload(notified_changeset.clone()) + .send(); + } + + send_notification(field_id, DatabaseNotification::DidUpdateField) + .payload(updated_field) + .send(); + } + Ok(()) +} diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/mod.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/mod.rs index da4ad5bc52..6522c8e917 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/mod.rs @@ -1,6 +1,7 @@ pub use layout_deps::*; pub use notifier::*; pub use view_editor::*; +pub use view_operation::*; pub use views::*; mod layout_deps; @@ -8,6 +9,7 @@ mod notifier; mod view_editor; mod view_filter; mod view_group; +mod view_operation; mod view_sort; mod views; // mod trait_impl; diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs index 4c5266c708..66d9760476 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs @@ -2,15 +2,13 @@ use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; -use collab_database::database::{gen_database_filter_id, gen_database_sort_id, MutexDatabase}; +use collab_database::database::{gen_database_filter_id, gen_database_sort_id}; use collab_database::fields::{Field, TypeOptionData}; -use collab_database::rows::{Cells, Row, RowCell, RowDetail, RowId}; -use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting}; +use collab_database::rows::{Cells, Row, RowDetail, RowId}; +use collab_database::views::{DatabaseLayout, DatabaseView}; use tokio::sync::{broadcast, RwLock}; use flowy_error::{FlowyError, FlowyResult}; -use flowy_task::TaskDispatcher; -use lib_infra::future::Fut; use crate::entities::{ CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams, @@ -25,124 +23,27 @@ use crate::services::database_view::view_filter::make_filter_controller; use crate::services::database_view::view_group::{ get_cell_for_row, get_cells_for_field, new_group_controller, new_group_controller_with_field, }; +use crate::services::database_view::view_operation::DatabaseViewOperation; use crate::services::database_view::view_sort::make_sort_controller; use crate::services::database_view::{ notify_did_update_filter, notify_did_update_group_rows, notify_did_update_num_of_groups, notify_did_update_setting, notify_did_update_sort, DatabaseLayoutDepsResolver, DatabaseViewChangedNotifier, DatabaseViewChangedReceiverRunner, }; -use crate::services::field::TypeOptionCellDataHandler; use crate::services::field_settings::FieldSettings; use crate::services::filter::{ Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType, }; use crate::services::group::{ - GroupChangeset, GroupChangesets, GroupController, GroupSetting, GroupSettingChangeset, - MoveGroupRowContext, RowChangeset, + GroupChangesets, GroupController, GroupSetting, GroupSettingChangeset, MoveGroupRowContext, + RowChangeset, }; use crate::services::setting::CalendarLayoutSetting; use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType}; -pub trait DatabaseViewData: Send + Sync + 'static { - fn get_database(&self) -> Arc; - - fn get_view(&self, view_id: &str) -> Fut>; - /// If the field_ids is None, then it will return all the field revisions - fn get_fields(&self, view_id: &str, field_ids: Option>) -> Fut>>; - - /// Returns the field with the field_id - fn get_field(&self, field_id: &str) -> Fut>>; - - fn create_field( - &self, - view_id: &str, - name: &str, - field_type: FieldType, - type_option_data: TypeOptionData, - ) -> Fut; - - fn get_primary_field(&self) -> Fut>>; - - /// Returns the index of the row with row_id - fn index_of_row(&self, view_id: &str, row_id: &RowId) -> Fut>; - - /// Returns the `index` and `RowRevision` with row_id - fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut)>>; - - /// Returns all the rows in the view - fn get_rows(&self, view_id: &str) -> Fut>>; - - fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut>>; - - fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut>; - - /// Return the database layout type for the view with given view_id - /// The default layout type is [DatabaseLayout::Grid] - fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout; - - fn get_group_setting(&self, view_id: &str) -> Vec; - - fn insert_group_setting(&self, view_id: &str, setting: GroupSetting); - - fn get_sort(&self, view_id: &str, sort_id: &str) -> Option; - - fn insert_sort(&self, view_id: &str, sort: Sort); - - fn remove_sort(&self, view_id: &str, sort_id: &str); - - fn get_all_sorts(&self, view_id: &str) -> Vec; - - fn remove_all_sorts(&self, view_id: &str); - - fn get_all_filters(&self, view_id: &str) -> Vec>; - - fn delete_filter(&self, view_id: &str, filter_id: &str); - - fn insert_filter(&self, view_id: &str, filter: Filter); - - fn get_filter(&self, view_id: &str, filter_id: &str) -> Option; - - fn get_filter_by_field_id(&self, view_id: &str, field_id: &str) -> Option; - - fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option; - - fn insert_layout_setting( - &self, - view_id: &str, - layout_ty: &DatabaseLayout, - layout_setting: LayoutSetting, - ); - - fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout); - - /// Returns a `TaskDispatcher` used to poll a `Task` - fn get_task_scheduler(&self) -> Arc>; - - fn get_type_option_cell_handler( - &self, - field: &Field, - field_type: &FieldType, - ) -> Option>; - - fn get_field_settings( - &self, - view_id: &str, - field_ids: &[String], - ) -> HashMap; - - fn get_all_field_settings(&self, view_id: &str) -> HashMap; - - fn update_field_settings( - &self, - view_id: &str, - field_id: &str, - visibility: Option, - ); -} - pub struct DatabaseViewEditor { pub view_id: String, - delegate: Arc, + delegate: Arc, group_controller: Arc>>>, filter_controller: Arc, sort_controller: Arc>, @@ -158,14 +59,17 @@ impl Drop for DatabaseViewEditor { impl DatabaseViewEditor { pub async fn new( view_id: String, - delegate: Arc, + delegate: Arc, cell_cache: CellCache, ) -> FlowyResult { let (notifier, _) = broadcast::channel(100); tokio::spawn(DatabaseViewChangedReceiverRunner(Some(notifier.subscribe())).run()); - let group_controller = new_group_controller(view_id.clone(), delegate.clone()).await?; - let group_controller = Arc::new(RwLock::new(group_controller)); + // Group + let group_controller = Arc::new(RwLock::new( + new_group_controller(view_id.clone(), delegate.clone()).await?, + )); + // Filter let filter_controller = make_filter_controller( &view_id, delegate.clone(), @@ -174,6 +78,7 @@ impl DatabaseViewEditor { ) .await; + // Sort let sort_controller = make_sort_controller( &view_id, delegate.clone(), @@ -198,7 +103,7 @@ impl DatabaseViewEditor { self.filter_controller.close().await; } - pub async fn get_view(&self) -> Option { + pub async fn v_get_view(&self) -> Option { self.delegate.get_view(&self.view_id).await } @@ -383,7 +288,7 @@ impl DatabaseViewEditor { let move_row_context = MoveGroupRowContext { row_detail, row_changeset, - field: field.as_ref(), + field: &field, to_group_id, to_row_id, }; @@ -485,40 +390,31 @@ impl DatabaseViewEditor { Ok(result.flatten()) } - pub async fn v_update_group_setting(&self, changeset: GroupChangesets) -> FlowyResult<()> { - self - .mut_group_controller(|group_controller, _| { - group_controller.apply_group_setting_changeset(changeset) - }) - .await; + pub async fn v_update_group(&self, changeset: GroupChangesets) -> FlowyResult<()> { + let mut type_option_data = TypeOptionData::new(); + let old_field = if let Some(controller) = self.group_controller.write().await.as_mut() { + let old_field = self.delegate.get_field(controller.field_id()); + type_option_data.extend(controller.apply_group_changeset(&changeset).await?); + old_field + } else { + None + }; + + if let Some(old_field) = old_field { + if !type_option_data.is_empty() { + self + .delegate + .update_field(&self.view_id, type_option_data, old_field) + .await?; + } + } + Ok(()) } pub async fn v_get_group_configuration_settings(&self) -> Vec { self.delegate.get_group_setting(&self.view_id) } - - pub async fn update_group( - &self, - changeset: GroupChangeset, - ) -> FlowyResult> { - match changeset.name { - Some(group_name) => { - let result = self - .mut_group_controller(|controller, _| { - Ok(controller.update_group_name(&changeset.group_id, &group_name)) - }) - .await; - - match result { - Some(r) => Ok(r), - None => Ok(None), - } - }, - None => Ok(None), - } - } - pub async fn v_get_all_sorts(&self) -> Vec { self.delegate.get_all_sorts(&self.view_id) } @@ -659,7 +555,7 @@ impl DatabaseViewEditor { if let Some(value) = self.delegate.get_layout_setting(&self.view_id, layout_ty) { let calendar_setting = CalendarLayoutSetting::from(value); // Check the field exist or not - if let Some(field) = self.delegate.get_field(&calendar_setting.field_id).await { + if let Some(field) = self.delegate.get_field(&calendar_setting.field_id) { let field_type = FieldType::from(field.field_type); // Check the type of field is Datetime or not @@ -682,11 +578,7 @@ impl DatabaseViewEditor { pub async fn v_set_layout_settings(&self, params: LayoutSettingParams) -> FlowyResult<()> { // Maybe it needs no send notification to refresh the UI if let Some(new_calendar_setting) = params.calendar { - if let Some(field) = self - .delegate - .get_field(&new_calendar_setting.field_id) - .await - { + if let Some(field) = self.delegate.get_field(&new_calendar_setting.field_id) { let field_type = FieldType::from(field.field_type); if field_type != FieldType::DateTime { return Err(FlowyError::unexpect_calendar_field_type()); @@ -729,13 +621,19 @@ impl DatabaseViewEditor { Ok(()) } + /// Notifies the view's field type-option data is changed + /// For the moment, only the groups will be generated after the type-option data changed. A + /// [Field] has a property named type_options contains a list of type-option data. #[tracing::instrument(level = "trace", skip_all, err)] - pub async fn v_did_update_field_type_option( - &self, - field_id: &str, - old_field: &Field, - ) -> FlowyResult<()> { - if let Some(field) = self.delegate.get_field(field_id).await { + pub async fn v_did_update_field_type_option(&self, old_field: &Field) -> FlowyResult<()> { + let field_id = &old_field.id; + // If the id of the grouping field is equal to the updated field's id, then we need to + // update the group setting + if self.is_grouping_field(field_id).await { + self.v_grouping_by_field(field_id).await?; + } + + if let Some(field) = self.delegate.get_field(field_id) { self .sort_controller .read() @@ -776,9 +674,13 @@ impl DatabaseViewEditor { /// Called when a grouping field is updated. #[tracing::instrument(level = "debug", skip_all, err)] pub async fn v_grouping_by_field(&self, field_id: &str) -> FlowyResult<()> { - if let Some(field) = self.delegate.get_field(field_id).await { - let new_group_controller = - new_group_controller_with_field(self.view_id.clone(), self.delegate.clone(), field).await?; + if let Some(field) = self.delegate.get_field(field_id) { + let new_group_controller = new_group_controller_with_field( + self.view_id.clone(), + self.delegate.clone(), + Arc::new(field), + ) + .await?; let new_groups = new_group_controller .groups() @@ -812,7 +714,7 @@ impl DatabaseViewEditor { let text_cell = get_cell_for_row(self.delegate.clone(), &primary_field.id, &row_id).await?; // Date - let date_field = self.delegate.get_field(&calendar_setting.field_id).await?; + let date_field = self.delegate.get_field(&calendar_setting.field_id)?; let date_cell = get_cell_for_row(self.delegate.clone(), &date_field.id, &row_id).await?; let title = text_cell @@ -973,7 +875,7 @@ impl DatabaseViewEditor { async fn mut_group_controller(&self, f: F) -> Option where - F: FnOnce(&mut Box, Arc) -> FlowyResult, + F: FnOnce(&mut Box, Field) -> FlowyResult, { let group_field_id = self .group_controller @@ -981,8 +883,7 @@ impl DatabaseViewEditor { .await .as_ref() .map(|group| group.field_id().to_owned())?; - let field = self.delegate.get_field(&group_field_id).await?; - + let field = self.delegate.get_field(&group_field_id)?; let mut write_guard = self.group_controller.write().await; if let Some(group_controller) = &mut *write_guard { f(group_controller, field).ok() diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_filter.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_filter.rs index eec723e6a0..75f31212d9 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_filter.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_filter.rs @@ -7,13 +7,13 @@ use lib_infra::future::{to_fut, Fut}; use crate::services::cell::CellCache; use crate::services::database_view::{ - gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData, + gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewOperation, }; use crate::services::filter::{Filter, FilterController, FilterDelegate, FilterTaskHandler}; pub async fn make_filter_controller( view_id: &str, - delegate: Arc, + delegate: Arc, notifier: DatabaseViewChangedNotifier, cell_cache: CellCache, ) -> Arc { @@ -43,7 +43,7 @@ pub async fn make_filter_controller( filter_controller } -struct DatabaseViewFilterDelegateImpl(Arc); +struct DatabaseViewFilterDelegateImpl(Arc); impl FilterDelegate for DatabaseViewFilterDelegateImpl { fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut>> { @@ -51,7 +51,7 @@ impl FilterDelegate for DatabaseViewFilterDelegateImpl { to_fut(async move { filter }) } - fn get_field(&self, field_id: &str) -> Fut>> { + fn get_field(&self, field_id: &str) -> Option { self.0.get_field(field_id) } diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs index b79ea3eb3e..2fb903b061 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs @@ -1,40 +1,43 @@ use std::sync::Arc; +use async_trait::async_trait; use collab_database::fields::Field; -use collab_database::rows::RowId; +use collab_database::rows::{Cell, RowId}; use flowy_error::FlowyResult; use lib_infra::future::{to_fut, Fut}; use crate::entities::FieldType; -use crate::services::database_view::DatabaseViewData; +use crate::services::database_view::DatabaseViewOperation; use crate::services::field::RowSingleCellData; use crate::services::group::{ find_new_grouping_field, make_group_controller, GroupController, GroupSetting, - GroupSettingReader, GroupSettingWriter, + GroupSettingReader, GroupSettingWriter, GroupTypeOptionCellOperation, }; pub async fn new_group_controller_with_field( view_id: String, - delegate: Arc, + delegate: Arc, grouping_field: Arc, ) -> FlowyResult> { let setting_reader = GroupSettingReaderImpl(delegate.clone()); let rows = delegate.get_rows(&view_id).await; let setting_writer = GroupSettingWriterImpl(delegate.clone()); + let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone()); make_group_controller( view_id, grouping_field, rows, setting_reader, setting_writer, + type_option_writer, ) .await } pub async fn new_group_controller( view_id: String, - delegate: Arc, + delegate: Arc, ) -> FlowyResult>> { let fields = delegate.get_fields(&view_id, None).await; let setting_reader = GroupSettingReaderImpl(delegate.clone()); @@ -59,6 +62,7 @@ pub async fn new_group_controller( if let Some(grouping_field) = grouping_field { let rows = delegate.get_rows(&view_id).await; let setting_writer = GroupSettingWriterImpl(delegate.clone()); + let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone()); Ok(Some( make_group_controller( view_id, @@ -66,6 +70,7 @@ pub async fn new_group_controller( rows, setting_reader, setting_writer, + type_option_writer, ) .await?, )) @@ -74,7 +79,7 @@ pub async fn new_group_controller( } } -pub(crate) struct GroupSettingReaderImpl(pub Arc); +pub(crate) struct GroupSettingReaderImpl(pub Arc); impl GroupSettingReader for GroupSettingReaderImpl { fn get_group_setting(&self, view_id: &str) -> Fut>> { @@ -97,11 +102,11 @@ impl GroupSettingReader for GroupSettingReaderImpl { } pub(crate) async fn get_cell_for_row( - delegate: Arc, + delegate: Arc, field_id: &str, row_id: &RowId, ) -> Option { - let field = delegate.get_field(field_id).await?; + let field = delegate.get_field(field_id)?; let row_cell = delegate.get_cell_in_row(field_id, row_id).await; let field_type = FieldType::from(field.field_type); let handler = delegate.get_type_option_cell_handler(&field, &field_type)?; @@ -120,11 +125,11 @@ pub(crate) async fn get_cell_for_row( // Returns the list of cells corresponding to the given field. pub(crate) async fn get_cells_for_field( - delegate: Arc, + delegate: Arc, view_id: &str, field_id: &str, ) -> Vec { - if let Some(field) = delegate.get_field(field_id).await { + if let Some(field) = delegate.get_field(field_id) { let field_type = FieldType::from(field.field_type); if let Some(handler) = delegate.get_type_option_cell_handler(&field, &field_type) { let cells = delegate.get_cells_for_field(view_id, field_id).await; @@ -149,11 +154,30 @@ pub(crate) async fn get_cells_for_field( vec![] } -struct GroupSettingWriterImpl(Arc); - +struct GroupSettingWriterImpl(Arc); impl GroupSettingWriter for GroupSettingWriterImpl { fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut> { self.0.insert_group_setting(view_id, group_setting); to_fut(async move { Ok(()) }) } } + +struct GroupTypeOptionCellWriterImpl(Arc); + +#[async_trait] +impl GroupTypeOptionCellOperation for GroupTypeOptionCellWriterImpl { + async fn get_cell(&self, _row_id: &RowId, _field_id: &str) -> FlowyResult> { + todo!() + } + + #[tracing::instrument(level = "trace", skip_all, err)] + async fn update_cell( + &self, + _view_id: &str, + _row_id: &RowId, + _field_id: &str, + _cell: Cell, + ) -> FlowyResult<()> { + todo!() + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_operation.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_operation.rs new file mode 100644 index 0000000000..77472a3452 --- /dev/null +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_operation.rs @@ -0,0 +1,126 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use collab_database::database::MutexDatabase; +use collab_database::fields::{Field, TypeOptionData}; +use collab_database::rows::{RowCell, RowDetail, RowId}; +use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting}; +use tokio::sync::RwLock; + +use flowy_error::FlowyError; +use flowy_task::TaskDispatcher; +use lib_infra::future::{Fut, FutureResult}; + +use crate::entities::{FieldType, FieldVisibility}; +use crate::services::field::TypeOptionCellDataHandler; +use crate::services::field_settings::FieldSettings; +use crate::services::filter::Filter; +use crate::services::group::GroupSetting; +use crate::services::sort::Sort; + +/// Defines the operation that can be performed on a database view +pub trait DatabaseViewOperation: Send + Sync + 'static { + /// Get the database that the view belongs to + fn get_database(&self) -> Arc; + + /// Get the view of the database with the view_id + fn get_view(&self, view_id: &str) -> Fut>; + /// If the field_ids is None, then it will return all the field revisions + fn get_fields(&self, view_id: &str, field_ids: Option>) -> Fut>>; + + /// Returns the field with the field_id + fn get_field(&self, field_id: &str) -> Option; + + fn create_field( + &self, + view_id: &str, + name: &str, + field_type: FieldType, + type_option_data: TypeOptionData, + ) -> Fut; + + fn update_field( + &self, + view_id: &str, + type_option_data: TypeOptionData, + old_field: Field, + ) -> FutureResult<(), FlowyError>; + + fn get_primary_field(&self) -> Fut>>; + + /// Returns the index of the row with row_id + fn index_of_row(&self, view_id: &str, row_id: &RowId) -> Fut>; + + /// Returns the `index` and `RowRevision` with row_id + fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut)>>; + + /// Returns all the rows in the view + fn get_rows(&self, view_id: &str) -> Fut>>; + + fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut>>; + + fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut>; + + /// Return the database layout type for the view with given view_id + /// The default layout type is [DatabaseLayout::Grid] + fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout; + + fn get_group_setting(&self, view_id: &str) -> Vec; + + fn insert_group_setting(&self, view_id: &str, setting: GroupSetting); + + fn get_sort(&self, view_id: &str, sort_id: &str) -> Option; + + fn insert_sort(&self, view_id: &str, sort: Sort); + + fn remove_sort(&self, view_id: &str, sort_id: &str); + + fn get_all_sorts(&self, view_id: &str) -> Vec; + + fn remove_all_sorts(&self, view_id: &str); + + fn get_all_filters(&self, view_id: &str) -> Vec>; + + fn delete_filter(&self, view_id: &str, filter_id: &str); + + fn insert_filter(&self, view_id: &str, filter: Filter); + + fn get_filter(&self, view_id: &str, filter_id: &str) -> Option; + + fn get_filter_by_field_id(&self, view_id: &str, field_id: &str) -> Option; + + fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option; + + fn insert_layout_setting( + &self, + view_id: &str, + layout_ty: &DatabaseLayout, + layout_setting: LayoutSetting, + ); + + fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout); + + /// Returns a `TaskDispatcher` used to poll a `Task` + fn get_task_scheduler(&self) -> Arc>; + + fn get_type_option_cell_handler( + &self, + field: &Field, + field_type: &FieldType, + ) -> Option>; + + fn get_field_settings( + &self, + view_id: &str, + field_ids: &[String], + ) -> HashMap; + + fn get_all_field_settings(&self, view_id: &str) -> HashMap; + + fn update_field_settings( + &self, + view_id: &str, + field_id: &str, + visibility: Option, + ); +} diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_sort.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_sort.rs index 6fec7d9ff9..6587d9ea0e 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_sort.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_sort.rs @@ -8,14 +8,14 @@ use lib_infra::future::{to_fut, Fut}; use crate::services::cell::CellCache; use crate::services::database_view::{ - gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData, + gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewOperation, }; use crate::services::filter::FilterController; use crate::services::sort::{Sort, SortController, SortDelegate, SortTaskHandler}; pub(crate) async fn make_sort_controller( view_id: &str, - delegate: Arc, + delegate: Arc, notifier: DatabaseViewChangedNotifier, filter_controller: Arc, cell_cache: CellCache, @@ -49,7 +49,7 @@ pub(crate) async fn make_sort_controller( } struct DatabaseViewSortDelegateImpl { - delegate: Arc, + delegate: Arc, filter_controller: Arc, } @@ -70,7 +70,7 @@ impl SortDelegate for DatabaseViewSortDelegateImpl { }) } - fn get_field(&self, field_id: &str) -> Fut>> { + fn get_field(&self, field_id: &str) -> Option { self.delegate.get_field(field_id) } diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs index 509ff5849b..8859422b9c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs @@ -2,47 +2,46 @@ use std::collections::HashMap; use std::sync::Arc; use collab_database::database::MutexDatabase; -use collab_database::fields::Field; use collab_database::rows::{RowDetail, RowId}; use nanoid::nanoid; use tokio::sync::{broadcast, RwLock}; -use flowy_error::FlowyResult; +use flowy_error::{FlowyError, FlowyResult}; use lib_infra::future::Fut; use crate::services::cell::CellCache; use crate::services::database::DatabaseRowEvent; -use crate::services::database_view::{DatabaseViewData, DatabaseViewEditor}; +use crate::services::database_view::{DatabaseViewEditor, DatabaseViewOperation}; use crate::services::group::RowChangeset; pub type RowEventSender = broadcast::Sender; pub type RowEventReceiver = broadcast::Receiver; - +pub type EditorByViewId = HashMap>; pub struct DatabaseViews { #[allow(dead_code)] database: Arc, cell_cache: CellCache, - database_view_data: Arc, - editor_map: Arc>>>, + view_operation: Arc, + editor_by_view_id: Arc>, } impl DatabaseViews { pub async fn new( database: Arc, cell_cache: CellCache, - database_view_data: Arc, + view_operation: Arc, + editor_by_view_id: Arc>, ) -> FlowyResult { - let editor_map = Arc::new(RwLock::new(HashMap::default())); Ok(Self { database, - database_view_data, + view_operation, cell_cache, - editor_map, + editor_by_view_id, }) } pub async fn close_view(&self, view_id: &str) -> bool { - let mut editor_map = self.editor_map.write().await; + let mut editor_map = self.editor_by_view_id.write().await; if let Some(view) = editor_map.remove(view_id) { view.close().await; } @@ -50,7 +49,13 @@ impl DatabaseViews { } pub async fn editors(&self) -> Vec> { - self.editor_map.read().await.values().cloned().collect() + self + .editor_by_view_id + .read() + .await + .values() + .cloned() + .collect() } /// It may generate a RowChangeset when the Row was moved from one group to another. @@ -77,43 +82,22 @@ impl DatabaseViews { Ok(()) } - /// Notifies the view's field type-option data is changed - /// For the moment, only the groups will be generated after the type-option data changed. A - /// [Field] has a property named type_options contains a list of type-option data. - /// # Arguments - /// - /// * `field_id`: the id of the field in current view - /// - #[tracing::instrument(level = "debug", skip(self, old_field), err)] - pub async fn did_update_field_type_option( - &self, - view_id: &str, - field_id: &str, - old_field: &Field, - ) -> FlowyResult<()> { - let view_editor = self.get_view_editor(view_id).await?; - // If the id of the grouping field is equal to the updated field's id, then we need to - // update the group setting - if view_editor.is_grouping_field(field_id).await { - view_editor.v_grouping_by_field(field_id).await?; - } - view_editor - .v_did_update_field_type_option(field_id, old_field) - .await?; - Ok(()) - } - pub async fn get_view_editor(&self, view_id: &str) -> FlowyResult> { debug_assert!(!view_id.is_empty()); - if let Some(editor) = self.editor_map.read().await.get(view_id) { + if let Some(editor) = self.editor_by_view_id.read().await.get(view_id) { return Ok(editor.clone()); } - let mut editor_map = self.editor_map.write().await; + let mut editor_map = self.editor_by_view_id.try_write().map_err(|err| { + FlowyError::internal().with_context(format!( + "fail to acquire the lock of editor_by_view_id: {}", + err + )) + })?; let editor = Arc::new( DatabaseViewEditor::new( view_id.to_owned(), - self.database_view_data.clone(), + self.view_operation.clone(), self.cell_cache.clone(), ) .await?, diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_ids.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_ids.rs index 1914414b91..55fe2635bc 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_ids.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_ids.rs @@ -24,7 +24,6 @@ impl SelectOptionIds { pub fn into_inner(self) -> Vec { self.0 } - pub fn to_cell_data(&self, field_type: FieldType) -> Cell { new_cell_builder(field_type) .insert_str_value(CELL_DATA, self.to_string()) diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs index 4e06f7b1af..7a5429076f 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs @@ -33,7 +33,15 @@ pub trait TypeOption { /// /// Uses `StrCellData` for any `TypeOption` if their cell data is pure `String`. /// - type CellData: TypeOptionCellData + ToString + Default + Send + Sync + Clone + Debug + 'static; + type CellData: for<'a> From<&'a Cell> + + TypeOptionCellData + + ToString + + Default + + Send + + Sync + + Clone + + Debug + + 'static; /// Represents as the corresponding field type cell changeset. /// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait. diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs index 52b7db8751..c07f8ff79f 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs @@ -113,16 +113,21 @@ where + Sync + 'static, { + pub fn into_boxed(self) -> Box { + Box::new(self) as Box + } + pub fn new_with_boxed( inner: T, cell_filter_cache: Option, cell_data_cache: Option, ) -> Box { - Box::new(Self { + Self { inner, cell_data_cache, cell_filter_cache, - }) as Box + } + .into_boxed() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs index 6d25e24c0c..ab6a56c45a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs @@ -21,7 +21,7 @@ use crate::services::filter::{Filter, FilterChangeset, FilterResult, FilterResul pub trait FilterDelegate: Send + Sync + 'static { fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut>>; - fn get_field(&self, field_id: &str) -> Fut>>; + fn get_field(&self, field_id: &str) -> Option; fn get_fields(&self, view_id: &str, field_ids: Option>) -> Fut>>; fn get_rows(&self, view_id: &str) -> Fut>>; fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut)>>; diff --git a/frontend/rust-lib/flowy-database2/src/services/group/action.rs b/frontend/rust-lib/flowy-database2/src/services/group/action.rs index 2163e840a1..376391dbd1 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/action.rs @@ -1,20 +1,22 @@ -use collab_database::fields::Field; +use async_trait::async_trait; +use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{Cell, Row, RowDetail}; use flowy_error::FlowyResult; use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; -use crate::services::cell::DecodedCellData; -use crate::services::group::controller::MoveGroupRowContext; +use crate::services::field::TypeOption; use crate::services::group::entities::GroupSetting; -use crate::services::group::{GroupChangesets, GroupData, GroupSettingChangeset}; +use crate::services::group::{ + GroupChangesets, GroupData, GroupSettingChangeset, MoveGroupRowContext, +}; /// Using polymorphism to provides the customs action for different group controller. /// /// For example, the `CheckboxGroupController` implements this trait to provide custom behavior. /// pub trait GroupCustomize: Send + Sync { - type CellData: DecodedCellData; + type GroupTypeOption: TypeOption; /// Returns the a value of the cell if the cell data is not exist. /// The default value is `None` /// @@ -26,13 +28,17 @@ pub trait GroupCustomize: Send + Sync { } /// Returns a bool value to determine whether the group should contain this cell or not. - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool; + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool; fn create_or_delete_group_when_cell_changed( &mut self, _row_detail: &RowDetail, - _old_cell_data: Option<&Self::CellData>, - _cell_data: &Self::CellData, + _old_cell_data: Option<&::CellProtobufType>, + _cell_data: &::CellProtobufType, ) -> FlowyResult<(Option, Option)> { Ok((None, None)) } @@ -43,16 +49,20 @@ pub trait GroupCustomize: Send + Sync { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec; /// Deletes the row from the group - fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec; + fn delete_row( + &mut self, + row: &Row, + cell_data: &::CellData, + ) -> Vec; /// Move row from one group to another fn move_row( &mut self, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, context: MoveGroupRowContext, ) -> Vec; @@ -60,13 +70,14 @@ pub trait GroupCustomize: Send + Sync { fn delete_group_when_move_row( &mut self, _row: &Row, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, ) -> Option { None } } /// Defines the shared actions any group controller can perform. +#[async_trait] pub trait GroupControllerOperation: Send + Sync { /// The field that is used for grouping the rows fn field_id(&self) -> &str; @@ -104,7 +115,10 @@ pub trait GroupControllerOperation: Send + Sync { /// Update the group if the corresponding field is changed fn did_update_group_field(&mut self, field: &Field) -> FlowyResult>; - fn apply_group_setting_changeset(&mut self, changeset: GroupChangesets) -> FlowyResult<()>; + async fn apply_group_changeset( + &mut self, + changeset: &GroupChangesets, + ) -> FlowyResult; fn apply_group_configuration_setting_changeset( &mut self, diff --git a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs index ed44bf0ed3..aca56b5495 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs @@ -3,10 +3,13 @@ use std::fmt::Formatter; use std::marker::PhantomData; use std::sync::Arc; +use async_trait::async_trait; use collab_database::fields::Field; +use collab_database::rows::{Cell, RowId}; use indexmap::IndexMap; use serde::de::DeserializeOwned; use serde::Serialize; +use tracing::event; use flowy_error::{FlowyError, FlowyResult}; use lib_infra::future::Fut; @@ -27,6 +30,18 @@ pub trait GroupSettingWriter: Send + Sync + 'static { fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut>; } +#[async_trait] +pub trait GroupTypeOptionCellOperation: Send + Sync + 'static { + async fn get_cell(&self, row_id: &RowId, field_id: &str) -> FlowyResult>; + async fn update_cell( + &self, + view_id: &str, + row_id: &RowId, + field_id: &str, + cell: Cell, + ) -> FlowyResult<()>; +} + impl std::fmt::Display for GroupContext { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.group_by_id.iter().for_each(|(_, group)| { @@ -64,7 +79,6 @@ pub struct GroupContext { /// A reader that implement the [GroupSettingReader] trait /// - #[allow(dead_code)] reader: Arc, /// A writer that implement the [GroupSettingWriter] trait is used to save the @@ -84,6 +98,7 @@ where reader: Arc, writer: Arc, ) -> FlowyResult { + event!(tracing::Level::TRACE, "GroupContext::new"); let setting = match reader.get_group_setting(&view_id).await { None => { let default_configuration = default_group_setting(&field); @@ -356,7 +371,7 @@ where } } - pub(crate) fn update_group(&mut self, group_changeset: GroupChangeset) -> FlowyResult<()> { + pub(crate) fn update_group(&mut self, group_changeset: &GroupChangeset) -> FlowyResult<()> { let update_group = self.mut_group(&group_changeset.group_id, |group| { if let Some(visible) = group_changeset.visible { group.visible = visible; diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs index 94af701498..77845f7338 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs @@ -1,9 +1,10 @@ -use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; +use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; -use collab_database::rows::{Cell, Cells, Row, RowDetail, RowId}; +use collab_database::rows::{Cells, Row, RowDetail}; +use futures::executor::block_on; use serde::de::DeserializeOwned; use serde::Serialize; @@ -12,13 +13,16 @@ use flowy_error::FlowyResult; use crate::entities::{ FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB, }; -use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser, DecodedCellData}; +use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser}; +use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData}; use crate::services::group::action::{ DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize, }; use crate::services::group::configuration::GroupContext; use crate::services::group::entities::{GroupData, GroupSetting}; -use crate::services::group::{Group, GroupChangesets, GroupSettingChangeset}; +use crate::services::group::{ + GroupChangeset, GroupChangesets, GroupSettingChangeset, GroupsBuilder, MoveGroupRowContext, +}; // use collab_database::views::Group; @@ -32,108 +36,67 @@ use crate::services::group::{Group, GroupChangesets, GroupSettingChangeset}; /// pub trait GroupController: GroupControllerOperation + Send + Sync { /// Called when the type option of the [Field] was updated. - fn did_update_field_type_option(&mut self, field: &Arc); + fn did_update_field_type_option(&mut self, field: &Field); /// Called before the row was created. fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str); /// Called after the row was created. fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str); - - /// Update group name handler - fn update_group_name(&mut self, _group_id: &str, _group_name: &str) -> Option { - None - } } -/// The [GroupsBuilder] trait is used to generate the groups for different [FieldType] -pub trait GroupsBuilder { - type Context; - type TypeOptionType; - - fn build( - field: &Field, - context: &Self::Context, - type_option: &Option, - ) -> GeneratedGroups; -} - -pub struct GeneratedGroups { - pub no_status_group: Option, - pub group_configs: Vec, -} - -pub struct GeneratedGroupConfig { - pub group: Group, - pub filter_content: String, -} - -pub struct MoveGroupRowContext<'a> { - pub row_detail: &'a RowDetail, - pub row_changeset: &'a mut RowChangeset, - pub field: &'a Field, - pub to_group_id: &'a str, - pub to_row_id: Option, -} - -#[derive(Debug, Clone)] -pub struct RowChangeset { - pub row_id: RowId, - pub height: Option, - pub visibility: Option, - // Contains the key/value changes represents as the update of the cells. For example, - // if there is one cell was changed, then the `cell_by_field_id` will only have one key/value. - pub cell_by_field_id: HashMap, -} - -impl RowChangeset { - pub fn new(row_id: RowId) -> Self { - Self { - row_id, - height: None, - visibility: None, - cell_by_field_id: Default::default(), - } - } - - pub fn is_empty(&self) -> bool { - self.height.is_none() && self.visibility.is_none() && self.cell_by_field_id.is_empty() - } +#[async_trait] +pub trait GroupOperationInterceptor { + type GroupTypeOption: TypeOption; + async fn type_option_from_group_changeset( + &self, + changeset: &GroupChangeset, + type_option: &Self::GroupTypeOption, + view_id: &str, + ) -> Option; } /// C: represents the group configuration that impl [GroupConfigurationSerde] /// T: the type-option data deserializer that impl [TypeOptionDataDeserializer] /// G: the group generator, [GroupsBuilder] /// P: the parser that impl [CellProtobufBlobParser] for the CellBytes -pub struct BaseGroupController { +pub struct BaseGroupController { pub grouping_field_id: String, - pub type_option: Option, + pub type_option: T, pub context: GroupContext, - group_action_phantom: PhantomData, + group_builder_phantom: PhantomData, cell_parser_phantom: PhantomData

, + pub operation_interceptor: I, } -impl BaseGroupController +impl BaseGroupController where C: Serialize + DeserializeOwned, - T: From, - G: GroupsBuilder, TypeOptionType = T>, + T: TypeOption + From + Send + Sync, + G: GroupsBuilder, GroupTypeOption = T>, + I: GroupOperationInterceptor + Send + Sync, { pub async fn new( grouping_field: &Arc, mut configuration: GroupContext, + operation_interceptor: I, ) -> FlowyResult { let field_type = FieldType::from(grouping_field.field_type); - let type_option = grouping_field.get_type_option::(field_type); - let generated_groups = G::build(grouping_field, &configuration, &type_option); + let type_option = grouping_field + .get_type_option::(&field_type) + .unwrap_or_else(|| T::from(default_type_option_data_from_type(&field_type))); + + // TODO(nathan): remove block_on + let generated_groups = block_on(G::build(grouping_field, &configuration, &type_option)); let _ = configuration.init_groups(generated_groups)?; Ok(Self { grouping_field_id: grouping_field.id.clone(), type_option, context: configuration, - group_action_phantom: PhantomData, + group_builder_phantom: PhantomData, cell_parser_phantom: PhantomData, + operation_interceptor, }) } @@ -209,14 +172,15 @@ where } } -impl GroupControllerOperation for BaseGroupController +#[async_trait] +impl GroupControllerOperation for BaseGroupController where - P: CellProtobufBlobParser, - C: Serialize + DeserializeOwned, - T: From, - G: GroupsBuilder, TypeOptionType = T>, - - Self: GroupCustomize, + P: CellProtobufBlobParser::CellProtobufType>, + C: Serialize + DeserializeOwned + Sync + Send, + T: TypeOption + From + Send + Sync, + G: GroupsBuilder, GroupTypeOption = T>, + I: GroupOperationInterceptor + Send + Sync, + Self: GroupCustomize, { fn field_id(&self) -> &str { &self.grouping_field_id @@ -232,7 +196,7 @@ where } #[tracing::instrument(level = "trace", skip_all, fields(row_count=%rows.len(), group_result))] - fn fill_groups(&mut self, rows: &[&RowDetail], field: &Field) -> FlowyResult<()> { + fn fill_groups(&mut self, rows: &[&RowDetail], _field: &Field) -> FlowyResult<()> { for row_detail in rows { let cell = match row_detail.row.cells.get(&self.grouping_field_id) { None => self.placeholder_cell(), @@ -241,8 +205,7 @@ where if let Some(cell) = cell { let mut grouped_rows: Vec = vec![]; - let cell_bytes = get_cell_protobuf(&cell, field, None); - let cell_data = cell_bytes.parser::

()?; + let cell_data = ::CellData::from(&cell); for group in self.context.groups() { if self.can_group(&group.filter_content, &cell_data) { grouped_rows.push(GroupedRow { @@ -320,7 +283,7 @@ where fn did_delete_delete_row( &mut self, row: &Row, - field: &Field, + _field: &Field, ) -> FlowyResult { // if the cell_rev is none, then the row must in the default group. let mut result = DidMoveGroupRowResult { @@ -328,9 +291,8 @@ where row_changesets: vec![], }; if let Some(cell) = row.cells.get(&self.grouping_field_id) { - let cell_bytes = get_cell_protobuf(cell, field, None); - let cell_data = cell_bytes.parser::

()?; - if !cell_data.is_empty() { + let cell_data = ::CellData::from(cell); + if !cell_data.is_cell_empty() { tracing::error!("did_delete_delete_row {:?}", cell); result.row_changesets = self.delete_row(row, &cell_data); return Ok(result); @@ -380,13 +342,24 @@ where Ok(None) } - fn apply_group_setting_changeset(&mut self, changeset: GroupChangesets) -> FlowyResult<()> { - for group_changeset in changeset.update_groups { - if let Err(e) = self.context.update_group(group_changeset) { - tracing::error!("Failed to update group: {:?}", e); + async fn apply_group_changeset( + &mut self, + changeset: &GroupChangesets, + ) -> FlowyResult { + for group_changeset in changeset.changesets.iter() { + self.context.update_group(group_changeset)?; + } + let mut type_option_data = TypeOptionData::new(); + for group_changeset in changeset.changesets.iter() { + if let Some(new_type_option_data) = self + .operation_interceptor + .type_option_from_group_changeset(group_changeset, &self.type_option, &self.context.view_id) + .await + { + type_option_data.extend(new_type_option_data); } } - Ok(()) + Ok(type_option_data) } fn apply_group_configuration_setting_changeset( diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs index 221bdbbea0..490b80dca5 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs @@ -1,20 +1,20 @@ -use std::sync::Arc; - -use collab_database::fields::Field; +use async_trait::async_trait; +use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use serde::{Deserialize, Serialize}; use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB}; use crate::services::cell::insert_checkbox_cell; use crate::services::field::{ - CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOption, CHECK, UNCHECK, + CheckboxCellDataParser, CheckboxTypeOption, TypeOption, CHECK, UNCHECK, }; use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{ - BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext, +use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::{ + move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, GroupChangeset, + GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, }; -use crate::services::group::{move_group_row, GeneratedGroupConfig, GeneratedGroups, Group}; #[derive(Default, Serialize, Deserialize)] pub struct CheckboxGroupConfiguration { @@ -24,14 +24,15 @@ pub struct CheckboxGroupConfiguration { pub type CheckboxGroupController = BaseGroupController< CheckboxGroupConfiguration, CheckboxTypeOption, - CheckboxGroupGenerator, + CheckboxGroupBuilder, CheckboxCellDataParser, + CheckboxGroupOperationInterceptorImpl, >; pub type CheckboxGroupContext = GroupContext; impl GroupCustomize for CheckboxGroupController { - type CellData = CheckboxCellData; + type GroupTypeOption = CheckboxTypeOption; fn placeholder_cell(&self) -> Option { Some( new_cell_builder(FieldType::Checkbox) @@ -40,7 +41,11 @@ impl GroupCustomize for CheckboxGroupController { ) } - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool { if cell_data.is_check() { content == CHECK } else { @@ -51,7 +56,7 @@ impl GroupCustomize for CheckboxGroupController { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { @@ -100,7 +105,11 @@ impl GroupCustomize for CheckboxGroupController { changesets } - fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec { + fn delete_row( + &mut self, + row: &Row, + _cell_data: &::CellData, + ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); @@ -118,7 +127,7 @@ impl GroupCustomize for CheckboxGroupController { fn move_row( &mut self, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -132,7 +141,7 @@ impl GroupCustomize for CheckboxGroupController { } impl GroupController for CheckboxGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) { + fn did_update_field_type_option(&mut self, _field: &Field) { // Do nothing } @@ -154,15 +163,16 @@ impl GroupController for CheckboxGroupController { } } -pub struct CheckboxGroupGenerator(); -impl GroupsBuilder for CheckboxGroupGenerator { +pub struct CheckboxGroupBuilder(); +#[async_trait] +impl GroupsBuilder for CheckboxGroupBuilder { type Context = CheckboxGroupContext; - type TypeOptionType = CheckboxTypeOption; + type GroupTypeOption = CheckboxTypeOption; - fn build( + async fn build( _field: &Field, _context: &Self::Context, - _type_option: &Option, + _type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { let check_group = GeneratedGroupConfig { group: Group::new(CHECK.to_string(), "".to_string()), @@ -180,3 +190,18 @@ impl GroupsBuilder for CheckboxGroupGenerator { } } } + +pub struct CheckboxGroupOperationInterceptorImpl {} + +#[async_trait] +impl GroupOperationInterceptor for CheckboxGroupOperationInterceptorImpl { + type GroupTypeOption = CheckboxTypeOption; + async fn type_option_from_group_changeset( + &self, + _changeset: &GroupChangeset, + _type_option: &Self::GroupTypeOption, + _view_id: &str, + ) -> Option { + todo!() + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs index 3c64e4ff26..1797a270b9 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs @@ -1,13 +1,13 @@ use std::format; use std::str::FromStr; -use std::sync::Arc; +use async_trait::async_trait; use chrono::{ DateTime, Datelike, Days, Duration, Local, NaiveDate, NaiveDateTime, Offset, TimeZone, }; use chrono_tz::Tz; use collab_database::database::timestamp; -use collab_database::fields::Field; +use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -15,18 +15,16 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use flowy_error::FlowyResult; use crate::entities::{ - DateCellDataPB, FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, - RowMetaPB, + FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB, }; use crate::services::cell::insert_date_cell; -use crate::services::field::{DateCellData, DateCellDataParser, DateTypeOption}; +use crate::services::field::{DateCellData, DateCellDataParser, DateTypeOption, TypeOption}; use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{ - BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext, -}; +use crate::services::group::controller::{BaseGroupController, GroupController}; use crate::services::group::{ make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, + GroupChangeset, GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, }; pub trait GroupConfigurationContentSerde: Sized + Send + Sync { @@ -63,14 +61,15 @@ pub enum DateCondition { pub type DateGroupController = BaseGroupController< DateGroupConfiguration, DateTypeOption, - DateGroupGenerator, + DateGroupBuilder, DateCellDataParser, + DateGroupOperationInterceptorImpl, >; pub type DateGroupContext = GroupContext; impl GroupCustomize for DateGroupController { - type CellData = DateCellDataPB; + type GroupTypeOption = DateTypeOption; fn placeholder_cell(&self) -> Option { Some( @@ -80,47 +79,48 @@ impl GroupCustomize for DateGroupController { ) } - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool { content == group_id( - &cell_data.into(), - self.type_option.as_ref(), + cell_data, + &self.type_option, &self.context.get_setting_content(), ) } fn create_or_delete_group_when_cell_changed( &mut self, - row_detail: &RowDetail, - old_cell_data: Option<&Self::CellData>, - cell_data: &Self::CellData, + _row_detail: &RowDetail, + _old_cell_data: Option<&::CellProtobufType>, + _cell_data: &::CellProtobufType, ) -> FlowyResult<(Option, Option)> { let setting_content = self.context.get_setting_content(); let mut inserted_group = None; if self .context .get_group(&group_id( - &cell_data.into(), - self.type_option.as_ref(), + &_cell_data.into(), + &self.type_option, &setting_content, )) .is_none() { - let group = make_group_from_date_cell( - &cell_data.into(), - self.type_option.as_ref(), - &setting_content, - ); + let group = + make_group_from_date_cell(&_cell_data.into(), &self.type_option, &setting_content); let mut new_group = self.context.add_new_group(group)?; - new_group.group.rows.push(RowMetaPB::from(row_detail)); + new_group.group.rows.push(RowMetaPB::from(_row_detail)); inserted_group = Some(new_group); } // Delete the old group if there are no rows in that group - let deleted_group = match old_cell_data.and_then(|old_cell_data| { + let deleted_group = match _old_cell_data.and_then(|old_cell_data| { self.context.get_group(&group_id( &old_cell_data.into(), - self.type_option.as_ref(), + &self.type_option, &setting_content, )) }) { @@ -148,19 +148,13 @@ impl GroupCustomize for DateGroupController { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec { let mut changesets = vec![]; let setting_content = self.context.get_setting_content(); self.context.iter_mut_status_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); - if group.id - == group_id( - &cell_data.into(), - self.type_option.as_ref(), - &setting_content, - ) - { + if group.id == group_id(&cell_data.into(), &self.type_option, &setting_content) { if !group.contains_row(&row_detail.row.id) { changeset .inserted_rows @@ -181,7 +175,11 @@ impl GroupCustomize for DateGroupController { changesets } - fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec { + fn delete_row( + &mut self, + row: &Row, + _cell_data: &::CellData, + ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); @@ -199,7 +197,7 @@ impl GroupCustomize for DateGroupController { fn move_row( &mut self, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -214,13 +212,13 @@ impl GroupCustomize for DateGroupController { fn delete_group_when_move_row( &mut self, _row: &Row, - cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, ) -> Option { let mut deleted_group = None; let setting_content = self.context.get_setting_content(); if let Some((_, group)) = self.context.get_group(&group_id( - &cell_data.into(), - self.type_option.as_ref(), + &_cell_data.into(), + &self.type_option, &setting_content, )) { if group.rows.len() == 1 { @@ -237,7 +235,7 @@ impl GroupCustomize for DateGroupController { } impl GroupController for DateGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) {} + fn did_update_field_type_option(&mut self, _field: &Field) {} fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { @@ -257,18 +255,19 @@ impl GroupController for DateGroupController { } } -pub struct DateGroupGenerator(); -impl GroupsBuilder for DateGroupGenerator { +pub struct DateGroupBuilder(); +#[async_trait] +impl GroupsBuilder for DateGroupBuilder { type Context = DateGroupContext; - type TypeOptionType = DateTypeOption; + type GroupTypeOption = DateTypeOption; - fn build( + async fn build( field: &Field, context: &Self::Context, - type_option: &Option, + type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { // Read all the cells for the grouping field - let cells = futures::executor::block_on(context.get_all_cells()); + let cells = context.get_all_cells().await; // Generate the groups let mut group_configs: Vec = cells @@ -276,8 +275,7 @@ impl GroupsBuilder for DateGroupGenerator { .flat_map(|value| value.into_date_field_cell_data()) .filter(|cell| cell.timestamp.is_some()) .map(|cell| { - let group = - make_group_from_date_cell(&cell, type_option.as_ref(), &context.get_setting_content()); + let group = make_group_from_date_cell(&cell, type_option, &context.get_setting_content()); GeneratedGroupConfig { filter_content: group.id.clone(), group, @@ -296,7 +294,7 @@ impl GroupsBuilder for DateGroupGenerator { fn make_group_from_date_cell( cell_data: &DateCellData, - type_option: Option<&DateTypeOption>, + type_option: &DateTypeOption, setting_content: &str, ) -> Group { let group_id = group_id(cell_data, type_option, setting_content); @@ -310,11 +308,9 @@ const GROUP_ID_DATE_FORMAT: &str = "%Y/%m/%d"; fn group_id( cell_data: &DateCellData, - type_option: Option<&DateTypeOption>, + type_option: &DateTypeOption, setting_content: &str, ) -> String { - let binding = DateTypeOption::default(); - let type_option = type_option.unwrap_or(&binding); let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default(); let date_time = date_time_from_timestamp(cell_data.timestamp, &type_option.timezone_id); @@ -373,11 +369,9 @@ fn group_id( fn group_name_from_id( group_id: &str, - type_option: Option<&DateTypeOption>, + type_option: &DateTypeOption, setting_content: &str, ) -> String { - let binding = DateTypeOption::default(); - let type_option = type_option.unwrap_or(&binding); let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default(); let date = NaiveDate::parse_from_str(group_id, GROUP_ID_DATE_FORMAT).unwrap(); @@ -449,6 +443,21 @@ fn date_time_from_timestamp(timestamp: Option, timezone_id: &str) -> DateTi } } +pub struct DateGroupOperationInterceptorImpl {} + +#[async_trait] +impl GroupOperationInterceptor for DateGroupOperationInterceptorImpl { + type GroupTypeOption = DateTypeOption; + async fn type_option_from_group_changeset( + &self, + _changeset: &GroupChangeset, + _type_option: &Self::GroupTypeOption, + _view_id: &str, + ) -> Option { + todo!() + } +} + #[cfg(test)] mod tests { use std::vec; @@ -582,16 +591,11 @@ mod tests { ]; for (i, test) in tests.iter().enumerate() { - let group_id = group_id( - &test.cell_data, - Some(test.type_option), - &test.setting_content, - ); + let group_id = group_id(&test.cell_data, test.type_option, &test.setting_content); assert_eq!(test.exp_group_id, group_id, "test {}", i); if !test.exp_group_name.is_empty() { - let group_name = - group_name_from_id(&group_id, Some(test.type_option), &test.setting_content); + let group_name = group_name_from_id(&group_id, test.type_option, &test.setting_content); assert_eq!(test.exp_group_name, group_name, "test {}", i); } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs index fe9c85f4e5..49c3c10147 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use collab_database::fields::Field; +use async_trait::async_trait; +use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{Cells, Row, RowDetail}; use flowy_error::FlowyResult; @@ -40,6 +41,7 @@ impl DefaultGroupController { } } +#[async_trait] impl GroupControllerOperation for DefaultGroupController { fn field_id(&self) -> &str { &self.field_id @@ -102,8 +104,11 @@ impl GroupControllerOperation for DefaultGroupController { Ok(None) } - fn apply_group_setting_changeset(&mut self, _changeset: GroupChangesets) -> FlowyResult<()> { - Ok(()) + async fn apply_group_changeset( + &mut self, + _changeset: &GroupChangesets, + ) -> FlowyResult { + Ok(TypeOptionData::default()) } fn apply_group_configuration_setting_changeset( @@ -115,7 +120,7 @@ impl GroupControllerOperation for DefaultGroupController { } impl GroupController for DefaultGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) { + fn did_update_field_type_option(&mut self, _field: &Field) { // Do nothing } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index 1fd9d3f2f6..c90c5df3d3 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -1,21 +1,20 @@ -use std::sync::Arc; - +use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use serde::{Deserialize, Serialize}; -use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB}; +use crate::entities::{FieldType, GroupRowsNotificationPB}; use crate::services::cell::insert_select_option_cell; use crate::services::field::{ MultiSelectTypeOption, SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, + TypeOption, }; use crate::services::group::action::GroupCustomize; -use crate::services::group::controller::{ - BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext, -}; +use crate::services::group::controller::{BaseGroupController, GroupController}; use crate::services::group::{ add_or_remove_select_option_row, generate_select_option_groups, make_no_status_group, - move_group_row, remove_select_option_row, GeneratedGroups, GroupContext, + move_group_row, remove_select_option_row, GeneratedGroups, GroupChangeset, GroupContext, + GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -28,18 +27,20 @@ pub type MultiSelectOptionGroupContext = GroupContext; impl GroupCustomize for MultiSelectGroupController { - type CellData = SelectOptionCellDataPB; + type GroupTypeOption = MultiSelectTypeOption; - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { - cell_data - .select_options - .iter() - .any(|option| option.id == content) + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool { + cell_data.iter().any(|option_id| option_id == content) } fn placeholder_cell(&self) -> Option { @@ -53,7 +54,7 @@ impl GroupCustomize for MultiSelectGroupController { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { @@ -64,7 +65,11 @@ impl GroupCustomize for MultiSelectGroupController { changesets } - fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec { + fn delete_row( + &mut self, + row: &Row, + cell_data: &::CellData, + ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row) { @@ -76,7 +81,7 @@ impl GroupCustomize for MultiSelectGroupController { fn move_row( &mut self, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -90,7 +95,7 @@ impl GroupCustomize for MultiSelectGroupController { } impl GroupController for MultiSelectGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) {} + fn did_update_field_type_option(&mut self, _field: &Field) {} fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { @@ -107,49 +112,56 @@ impl GroupController for MultiSelectGroupController { group.add_row(row_detail.clone()) } } - - fn update_group_name(&mut self, group_id: &str, group_name: &str) -> Option { - match &self.type_option { - Some(type_option) => { - let select_option = type_option - .options - .iter() - .find(|option| option.id == group_id) - .unwrap(); - - let new_select_option = SelectOption { - name: group_name.to_owned(), - ..select_option.to_owned() - }; - - let mut new_type_option = type_option.clone(); - new_type_option.insert_option(new_select_option); - - Some(new_type_option.to_type_option_data()) - }, - None => None, - } - } } -pub struct MultiSelectGroupGenerator; -impl GroupsBuilder for MultiSelectGroupGenerator { +pub struct MultiSelectGroupBuilder; +#[async_trait] +impl GroupsBuilder for MultiSelectGroupBuilder { type Context = MultiSelectOptionGroupContext; - type TypeOptionType = MultiSelectTypeOption; + type GroupTypeOption = MultiSelectTypeOption; - fn build( + async fn build( field: &Field, _context: &Self::Context, - type_option: &Option, + type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { - let group_configs = match type_option { - None => vec![], - Some(type_option) => generate_select_option_groups(&field.id, &type_option.options), - }; - + let group_configs = generate_select_option_groups(&field.id, &type_option.options); GeneratedGroups { no_status_group: Some(make_no_status_group(field)), group_configs, } } } + +pub struct MultiSelectGroupOperationInterceptorImpl; + +#[async_trait] +impl GroupOperationInterceptor for MultiSelectGroupOperationInterceptorImpl { + type GroupTypeOption = MultiSelectTypeOption; + + #[tracing::instrument(level = "trace", skip_all)] + async fn type_option_from_group_changeset( + &self, + changeset: &GroupChangeset, + type_option: &Self::GroupTypeOption, + _view_id: &str, + ) -> Option { + if let Some(name) = &changeset.name { + let mut new_type_option = type_option.clone(); + let select_option = type_option + .options + .iter() + .find(|option| option.id == changeset.group_id) + .unwrap(); + + let new_select_option = SelectOption { + name: name.to_owned(), + ..select_option.to_owned() + }; + new_type_option.insert_option(new_select_option); + return Some(new_type_option.into()); + } + + None + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index c00ab4c3a1..de94d8e1d0 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -1,21 +1,22 @@ -use std::sync::Arc; - +use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use serde::{Deserialize, Serialize}; -use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB}; +use crate::entities::{FieldType, GroupRowsNotificationPB}; use crate::services::cell::insert_select_option_cell; use crate::services::field::{ SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption, + TypeOption, }; use crate::services::group::action::GroupCustomize; -use crate::services::group::controller::{ - BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext, -}; +use crate::services::group::controller::{BaseGroupController, GroupController}; use crate::services::group::controller_impls::select_option_controller::util::*; use crate::services::group::entities::GroupData; -use crate::services::group::{make_no_status_group, GeneratedGroups, GroupContext}; +use crate::services::group::{ + make_no_status_group, GeneratedGroups, GroupChangeset, GroupContext, GroupOperationInterceptor, + GroupsBuilder, MoveGroupRowContext, +}; #[derive(Default, Serialize, Deserialize)] pub struct SingleSelectGroupConfiguration { @@ -28,17 +29,19 @@ pub type SingleSelectOptionGroupContext = GroupContext; impl GroupCustomize for SingleSelectGroupController { - type CellData = SelectOptionCellDataPB; - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { - cell_data - .select_options - .iter() - .any(|option| option.id == content) + type GroupTypeOption = SingleSelectTypeOption; + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool { + cell_data.iter().any(|option_id| option_id == content) } fn placeholder_cell(&self) -> Option { @@ -52,7 +55,7 @@ impl GroupCustomize for SingleSelectGroupController { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { @@ -63,7 +66,11 @@ impl GroupCustomize for SingleSelectGroupController { changesets } - fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec { + fn delete_row( + &mut self, + row: &Row, + cell_data: &::CellData, + ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row) { @@ -75,7 +82,7 @@ impl GroupCustomize for SingleSelectGroupController { fn move_row( &mut self, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -89,7 +96,7 @@ impl GroupCustomize for SingleSelectGroupController { } impl GroupController for SingleSelectGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) {} + fn did_update_field_type_option(&mut self, _field: &Field) {} fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { let group: Option<&mut GroupData> = self.context.get_mut_group(group_id); @@ -107,44 +114,19 @@ impl GroupController for SingleSelectGroupController { group.add_row(row_detail.clone()) } } - - fn update_group_name(&mut self, group_id: &str, group_name: &str) -> Option { - match &self.type_option { - Some(type_option) => { - let select_option = type_option - .options - .iter() - .find(|option| option.id == group_id) - .unwrap(); - - let new_select_option = SelectOption { - name: group_name.to_owned(), - ..select_option.to_owned() - }; - - let mut new_type_option = type_option.clone(); - new_type_option.insert_option(new_select_option); - - Some(new_type_option.to_type_option_data()) - }, - None => None, - } - } } -pub struct SingleSelectGroupGenerator(); -impl GroupsBuilder for SingleSelectGroupGenerator { +pub struct SingleSelectGroupBuilder(); +#[async_trait] +impl GroupsBuilder for SingleSelectGroupBuilder { type Context = SingleSelectOptionGroupContext; - type TypeOptionType = SingleSelectTypeOption; - fn build( + type GroupTypeOption = SingleSelectTypeOption; + async fn build( field: &Field, _context: &Self::Context, - type_option: &Option, + type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { - let group_configs = match type_option { - None => vec![], - Some(type_option) => generate_select_option_groups(&field.id, &type_option.options), - }; + let group_configs = generate_select_option_groups(&field.id, &type_option.options); GeneratedGroups { no_status_group: Some(make_no_status_group(field)), @@ -152,3 +134,36 @@ impl GroupsBuilder for SingleSelectGroupGenerator { } } } + +pub struct SingleSelectGroupOperationInterceptorImpl; + +#[async_trait] +impl GroupOperationInterceptor for SingleSelectGroupOperationInterceptorImpl { + type GroupTypeOption = SingleSelectTypeOption; + + #[tracing::instrument(level = "trace", skip_all)] + async fn type_option_from_group_changeset( + &self, + changeset: &GroupChangeset, + type_option: &Self::GroupTypeOption, + _view_id: &str, + ) -> Option { + if let Some(name) = &changeset.name { + let mut new_type_option = type_option.clone(); + let select_option = type_option + .options + .iter() + .find(|option| option.id == changeset.group_id) + .unwrap(); + + let new_select_option = SelectOption { + name: name.to_owned(), + ..select_option.to_owned() + }; + new_type_option.insert_option(new_select_option); + return Some(new_type_option.into()); + } + + None + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs index 2251a4ae06..e68b1be0ed 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs @@ -8,9 +8,8 @@ use crate::entities::{ use crate::services::cell::{ insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell, }; -use crate::services::field::{SelectOption, CHECK}; -use crate::services::group::controller::MoveGroupRowContext; -use crate::services::group::{GeneratedGroupConfig, Group, GroupData}; +use crate::services::field::{SelectOption, SelectOptionIds, CHECK}; +use crate::services::group::{GeneratedGroupConfig, Group, GroupData, MoveGroupRowContext}; pub fn add_or_remove_select_option_row( group: &mut GroupData, @@ -52,12 +51,12 @@ pub fn add_or_remove_select_option_row( pub fn remove_select_option_row( group: &mut GroupData, - cell_data: &SelectOptionCellDataPB, + cell_data: &SelectOptionIds, row: &Row, ) -> Option { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); - cell_data.select_options.iter().for_each(|option| { - if option.id == group.id && group.contains_row(&row.id) { + cell_data.iter().for_each(|option_id| { + if option_id == &group.id && group.contains_row(&row.id) { group.remove_row(&row.id); changeset.deleted_rows.push(row.id.clone().into_inner()); } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs index f08ab426d5..4e85557101 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use collab_database::fields::Field; +use async_trait::async_trait; +use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; use serde::{Deserialize, Serialize}; @@ -8,17 +9,16 @@ use flowy_error::FlowyResult; use crate::entities::{ FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB, - URLCellDataPB, }; use crate::services::cell::insert_url_cell; -use crate::services::field::{URLCellData, URLCellDataParser, URLTypeOption}; +use crate::services::field::{TypeOption, URLCellData, URLCellDataParser, URLTypeOption}; use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{ - BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext, -}; +use crate::services::group::controller::{BaseGroupController, GroupController}; use crate::services::group::{ make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, + GroupChangeset, GroupOperationInterceptor, GroupTypeOptionCellOperation, GroupsBuilder, + MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -26,13 +26,18 @@ pub struct URLGroupConfiguration { pub hide_empty: bool, } -pub type URLGroupController = - BaseGroupController; +pub type URLGroupController = BaseGroupController< + URLGroupConfiguration, + URLTypeOption, + URLGroupGenerator, + URLCellDataParser, + URLGroupOperationInterceptorImpl, +>; pub type URLGroupContext = GroupContext; impl GroupCustomize for URLGroupController { - type CellData = URLCellDataPB; + type GroupTypeOption = URLTypeOption; fn placeholder_cell(&self) -> Option { Some( @@ -42,15 +47,19 @@ impl GroupCustomize for URLGroupController { ) } - fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { - cell_data.content == content + fn can_group( + &self, + content: &str, + cell_data: &::CellData, + ) -> bool { + cell_data.data == content } fn create_or_delete_group_when_cell_changed( &mut self, - row_detail: &RowDetail, - _old_cell_data: Option<&Self::CellData>, - _cell_data: &Self::CellData, + _row_detail: &RowDetail, + _old_cell_data: Option<&::CellProtobufType>, + _cell_data: &::CellProtobufType, ) -> FlowyResult<(Option, Option)> { // Just return if the group with this url already exists let mut inserted_group = None; @@ -58,7 +67,7 @@ impl GroupCustomize for URLGroupController { let cell_data: URLCellData = _cell_data.clone().into(); let group = make_group_from_url_cell(&cell_data); let mut new_group = self.context.add_new_group(group)?; - new_group.group.rows.push(RowMetaPB::from(row_detail)); + new_group.group.rows.push(RowMetaPB::from(_row_detail)); inserted_group = Some(new_group); } @@ -90,7 +99,7 @@ impl GroupCustomize for URLGroupController { fn add_or_remove_row_when_cell_changed( &mut self, row_detail: &RowDetail, - cell_data: &Self::CellData, + cell_data: &::CellProtobufType, ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_status_groups(|group| { @@ -116,7 +125,11 @@ impl GroupCustomize for URLGroupController { changesets } - fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec { + fn delete_row( + &mut self, + row: &Row, + _cell_data: &::CellData, + ) -> Vec { let mut changesets = vec![]; self.context.iter_mut_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); @@ -134,7 +147,7 @@ impl GroupCustomize for URLGroupController { fn move_row( &mut self, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -145,11 +158,10 @@ impl GroupCustomize for URLGroupController { }); group_changeset } - fn delete_group_when_move_row( &mut self, _row: &Row, - _cell_data: &Self::CellData, + _cell_data: &::CellProtobufType, ) -> Option { let mut deleted_group = None; if let Some((_, group)) = self.context.get_group(&_cell_data.content) { @@ -167,7 +179,7 @@ impl GroupCustomize for URLGroupController { } impl GroupController for URLGroupController { - fn did_update_field_type_option(&mut self, _field: &Arc) {} + fn did_update_field_type_option(&mut self, _field: &Field) {} fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { @@ -187,17 +199,18 @@ impl GroupController for URLGroupController { } pub struct URLGroupGenerator(); +#[async_trait] impl GroupsBuilder for URLGroupGenerator { type Context = URLGroupContext; - type TypeOptionType = URLTypeOption; + type GroupTypeOption = URLTypeOption; - fn build( + async fn build( field: &Field, context: &Self::Context, - _type_option: &Option, + _type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { // Read all the cells for the grouping field - let cells = futures::executor::block_on(context.get_all_cells()); + let cells = context.get_all_cells().await; // Generate the groups let group_configs = cells @@ -223,3 +236,21 @@ fn make_group_from_url_cell(cell: &URLCellData) -> Group { let group_name = cell.data.clone(); Group::new(group_id, group_name) } + +pub struct URLGroupOperationInterceptorImpl { + #[allow(dead_code)] + pub(crate) cell_writer: Arc, +} + +#[async_trait::async_trait] +impl GroupOperationInterceptor for URLGroupOperationInterceptorImpl { + type GroupTypeOption = URLTypeOption; + async fn type_option_from_group_changeset( + &self, + _changeset: &GroupChangeset, + _type_option: &Self::GroupTypeOption, + _view_id: &str, + ) -> Option { + todo!() + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/entities.rs b/frontend/rust-lib/flowy-database2/src/services/group/entities.rs index 72c35c91b9..f7427f9510 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/entities.rs @@ -20,7 +20,13 @@ pub struct GroupSettingChangeset { } pub struct GroupChangesets { - pub update_groups: Vec, + pub changesets: Vec, +} + +impl From> for GroupChangesets { + fn from(changesets: Vec) -> Self { + Self { changesets } + } } #[derive(Clone, Default, Debug)] diff --git a/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs b/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs index bc610302be..36f0c5b568 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs @@ -1,19 +1,82 @@ +use std::collections::HashMap; use std::sync::Arc; +use async_trait::async_trait; use collab_database::fields::Field; -use collab_database::rows::RowDetail; +use collab_database::rows::{Cell, RowDetail, RowId}; use collab_database::views::DatabaseLayout; use flowy_error::FlowyResult; use crate::entities::FieldType; +use crate::services::field::TypeOption; use crate::services::group::{ - CheckboxGroupContext, CheckboxGroupController, DateGroupContext, DateGroupController, - DefaultGroupController, Group, GroupController, GroupSetting, GroupSettingReader, - GroupSettingWriter, MultiSelectGroupController, MultiSelectOptionGroupContext, - SingleSelectGroupController, SingleSelectOptionGroupContext, URLGroupContext, URLGroupController, + CheckboxGroupContext, CheckboxGroupController, CheckboxGroupOperationInterceptorImpl, + DateGroupContext, DateGroupController, DateGroupOperationInterceptorImpl, DefaultGroupController, + Group, GroupController, GroupSetting, GroupSettingReader, GroupSettingWriter, + GroupTypeOptionCellOperation, MultiSelectGroupController, + MultiSelectGroupOperationInterceptorImpl, MultiSelectOptionGroupContext, + SingleSelectGroupController, SingleSelectGroupOperationInterceptorImpl, + SingleSelectOptionGroupContext, URLGroupContext, URLGroupController, + URLGroupOperationInterceptorImpl, }; +/// The [GroupsBuilder] trait is used to generate the groups for different [FieldType] +#[async_trait] +pub trait GroupsBuilder: Send + Sync + 'static { + type Context; + type GroupTypeOption: TypeOption; + + async fn build( + field: &Field, + context: &Self::Context, + type_option: &Self::GroupTypeOption, + ) -> GeneratedGroups; +} + +pub struct GeneratedGroups { + pub no_status_group: Option, + pub group_configs: Vec, +} + +pub struct GeneratedGroupConfig { + pub group: Group, + pub filter_content: String, +} + +pub struct MoveGroupRowContext<'a> { + pub row_detail: &'a RowDetail, + pub row_changeset: &'a mut RowChangeset, + pub field: &'a Field, + pub to_group_id: &'a str, + pub to_row_id: Option, +} + +#[derive(Debug, Clone)] +pub struct RowChangeset { + pub row_id: RowId, + pub height: Option, + pub visibility: Option, + // Contains the key/value changes represents as the update of the cells. For example, + // if there is one cell was changed, then the `cell_by_field_id` will only have one key/value. + pub cell_by_field_id: HashMap, +} + +impl RowChangeset { + pub fn new(row_id: RowId) -> Self { + Self { + row_id, + height: None, + visibility: None, + cell_by_field_id: Default::default(), + } + } + + pub fn is_empty(&self) -> bool { + self.height.is_none() && self.visibility.is_none() && self.cell_by_field_id.is_empty() + } +} + /// Returns a group controller. /// /// Each view can be grouped by one field, each field has its own group controller. @@ -31,16 +94,18 @@ use crate::services::group::{ fields(grouping_field_id=%grouping_field.id, grouping_field_type) err )] -pub async fn make_group_controller( +pub async fn make_group_controller( view_id: String, grouping_field: Arc, row_details: Vec>, setting_reader: R, setting_writer: W, + type_option_cell_writer: TW, ) -> FlowyResult> where R: GroupSettingReader, W: GroupSettingWriter, + TW: GroupTypeOptionCellOperation, { let grouping_field_type = FieldType::from(grouping_field.field_type); tracing::Span::current().record("grouping_field", &grouping_field_type.default_name()); @@ -48,6 +113,7 @@ where let mut group_controller: Box; let configuration_reader = Arc::new(setting_reader); let configuration_writer = Arc::new(setting_writer); + let type_option_cell_writer = Arc::new(type_option_cell_writer); match grouping_field_type { FieldType::SingleSelect => { @@ -58,7 +124,10 @@ where configuration_writer, ) .await?; - let controller = SingleSelectGroupController::new(&grouping_field, configuration).await?; + let operation_interceptor = SingleSelectGroupOperationInterceptorImpl; + let controller = + SingleSelectGroupController::new(&grouping_field, configuration, operation_interceptor) + .await?; group_controller = Box::new(controller); }, FieldType::MultiSelect => { @@ -69,7 +138,10 @@ where configuration_writer, ) .await?; - let controller = MultiSelectGroupController::new(&grouping_field, configuration).await?; + let operation_interceptor = MultiSelectGroupOperationInterceptorImpl; + let controller = + MultiSelectGroupController::new(&grouping_field, configuration, operation_interceptor) + .await?; group_controller = Box::new(controller); }, FieldType::Checkbox => { @@ -80,7 +152,9 @@ where configuration_writer, ) .await?; - let controller = CheckboxGroupController::new(&grouping_field, configuration).await?; + let operation_interceptor = CheckboxGroupOperationInterceptorImpl {}; + let controller = + CheckboxGroupController::new(&grouping_field, configuration, operation_interceptor).await?; group_controller = Box::new(controller); }, FieldType::URL => { @@ -91,7 +165,11 @@ where configuration_writer, ) .await?; - let controller = URLGroupController::new(&grouping_field, configuration).await?; + let operation_interceptor = URLGroupOperationInterceptorImpl { + cell_writer: type_option_cell_writer, + }; + let controller = + URLGroupController::new(&grouping_field, configuration, operation_interceptor).await?; group_controller = Box::new(controller); }, FieldType::DateTime => { @@ -102,7 +180,9 @@ where configuration_writer, ) .await?; - let controller = DateGroupController::new(&grouping_field, configuration).await?; + let operation_interceptor = DateGroupOperationInterceptorImpl {}; + let controller = + DateGroupController::new(&grouping_field, configuration, operation_interceptor).await?; group_controller = Box::new(controller); }, _ => { diff --git a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs index 3540ba9c23..fe4cf2d55c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs @@ -26,7 +26,7 @@ pub trait SortDelegate: Send + Sync { fn get_sort(&self, view_id: &str, sort_id: &str) -> Fut>>; /// Returns all the rows after applying grid's filter fn get_rows(&self, view_id: &str) -> Fut>>; - fn get_field(&self, field_id: &str) -> Fut>>; + fn get_field(&self, field_id: &str) -> Option; fn get_fields(&self, view_id: &str, field_ids: Option>) -> Fut>>; } diff --git a/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs b/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs index 55ab2bc1d4..a8a39c645a 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs @@ -10,7 +10,6 @@ use flowy_database2::services::field::{ edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction, SingleSelectTypeOption, }; -use flowy_database2::services::group::GroupSettingChangeset; use lib_infra::util::timestamp; use crate::database::database_editor::DatabaseEditorTest; @@ -68,12 +67,6 @@ pub enum GroupScript { group_id: String, group_name: String, }, - AssertGroupConfiguration { - hide_ungrouped: bool, - }, - UpdateGroupConfiguration { - hide_ungrouped: Option, - }, } pub struct DatabaseGroupTest { @@ -276,25 +269,6 @@ impl DatabaseGroupTest { assert_eq!(group_id, group.group_id, "group index: {}", group_index); assert_eq!(group_name, group.group_name, "group index: {}", group_index); }, - GroupScript::AssertGroupConfiguration { hide_ungrouped } => { - let group_configuration = self - .editor - .get_group_configuration_settings(&self.view_id) - .await - .unwrap(); - let group_configuration = group_configuration.get(0).unwrap(); - assert_eq!(group_configuration.hide_ungrouped, hide_ungrouped); - }, - GroupScript::UpdateGroupConfiguration { hide_ungrouped } => { - self - .editor - .update_group_configuration_setting( - &self.view_id, - GroupSettingChangeset { hide_ungrouped }, - ) - .await - .unwrap(); - }, } } diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml index ac48436bb7..2a06f299ef 100644 --- a/frontend/rust-lib/flowy-error/Cargo.toml +++ b/frontend/rust-lib/flowy-error/Cargo.toml @@ -12,6 +12,7 @@ bytes = "1.4" anyhow = "1.0" thiserror = "1.0" validator = "0.16.0" +tokio = { version = "1.0", features = ["sync"]} fancy-regex = { version = "0.11.0" } lib-dispatch = { workspace = true, optional = true } diff --git a/frontend/rust-lib/flowy-error/src/errors.rs b/frontend/rust-lib/flowy-error/src/errors.rs index d5a462058d..358ed13eb8 100644 --- a/frontend/rust-lib/flowy-error/src/errors.rs +++ b/frontend/rust-lib/flowy-error/src/errors.rs @@ -157,3 +157,9 @@ impl From for FlowyError { FlowyError::internal().with_context(e) } } + +impl From for FlowyError { + fn from(e: tokio::sync::oneshot::error::RecvError) -> Self { + FlowyError::internal().with_context(e) + } +}