mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: rename group (#3815)
* chore: add group operation interceptor * refactor: impl interceptor trait * chore: update type option when group change * test: fix test
This commit is contained in:
21
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
21
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -454,7 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"borsh-derive",
|
"borsh-derive",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -854,7 +854,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -873,7 +873,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -903,7 +903,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -915,7 +915,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -935,7 +935,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -949,7 +949,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -991,7 +991,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1012,7 +1012,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1039,7 +1039,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -2115,6 +2115,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"url",
|
"url",
|
||||||
"validator",
|
"validator",
|
||||||
|
@ -48,14 +48,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { 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 = "ff10abd5" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
21
frontend/rust-lib/Cargo.lock
generated
21
frontend/rust-lib/Cargo.lock
generated
@ -461,7 +461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"borsh-derive",
|
"borsh-derive",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -721,7 +721,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -740,7 +740,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -770,7 +770,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -782,7 +782,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -802,7 +802,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -816,7 +816,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -858,7 +858,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -879,7 +879,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -906,7 +906,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1938,6 +1938,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"url",
|
"url",
|
||||||
"validator",
|
"validator",
|
||||||
|
@ -92,11 +92,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { 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 = "ff10abd5e" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "ff10abd5e" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8861d7a" }
|
||||||
|
@ -761,7 +761,7 @@ async fn rename_group_event_test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn hide_group_event_test2() {
|
async fn hide_group_event_test() {
|
||||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||||
let current_workspace = test.get_current_workspace().await.workspace;
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
let board_view = test
|
let board_view = test
|
||||||
|
@ -2,6 +2,7 @@ use std::sync::{Arc, Weak};
|
|||||||
|
|
||||||
use collab_database::database::gen_row_id;
|
use collab_database::database::gen_row_id;
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
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,
|
type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset,
|
||||||
};
|
};
|
||||||
use crate::services::field_settings::FieldSettingsChangesetParams;
|
use crate::services::field_settings::FieldSettingsChangesetParams;
|
||||||
use crate::services::group::{GroupChangeset, GroupChangesets};
|
use crate::services::group::GroupChangeset;
|
||||||
use crate::services::share::csv::CSVFormat;
|
use crate::services::share::csv::CSVFormat;
|
||||||
|
|
||||||
fn upgrade_manager(
|
fn upgrade_manager(
|
||||||
@ -725,18 +726,15 @@ pub(crate) async fn update_group_handler(
|
|||||||
let view_id = params.view_id.clone();
|
let view_id = params.view_id.clone();
|
||||||
let database_editor = manager.get_database_with_view_id(&view_id).await?;
|
let database_editor = manager.get_database_with_view_id(&view_id).await?;
|
||||||
let group_changeset = GroupChangeset::from(params);
|
let group_changeset = GroupChangeset::from(params);
|
||||||
database_editor
|
let (tx, rx) = oneshot::channel();
|
||||||
.update_group(&view_id, group_changeset.clone())
|
tokio::spawn(async move {
|
||||||
.await?;
|
let result = database_editor
|
||||||
database_editor
|
.update_group(&view_id, vec![group_changeset].into())
|
||||||
.update_group_setting(
|
.await;
|
||||||
&view_id,
|
let _ = tx.send(result);
|
||||||
GroupChangesets {
|
});
|
||||||
update_groups: vec![group_changeset],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
|
let _ = rx.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,11 @@ use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowDetai
|
|||||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
|
use tracing::{event, warn};
|
||||||
|
|
||||||
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_task::TaskDispatcher;
|
use flowy_task::TaskDispatcher;
|
||||||
use lib_infra::future::{to_fut, Fut};
|
use lib_infra::future::{to_fut, Fut, FutureResult};
|
||||||
|
|
||||||
use crate::entities::*;
|
use crate::entities::*;
|
||||||
use crate::notification::{send_notification, DatabaseNotification};
|
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::util::database_view_setting_pb_from_view;
|
||||||
use crate::services::database::UpdatedRow;
|
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::checklist_type_option::ChecklistCellChangeset;
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
default_type_option_data_from_type, select_type_option_from_field, transform_type_option,
|
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::filter::Filter;
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
default_group_setting, GroupChangeset, GroupChangesets, GroupSetting, GroupSettingChangeset,
|
default_group_setting, GroupChangesets, GroupSetting, GroupSettingChangeset, RowChangeset,
|
||||||
RowChangeset,
|
|
||||||
};
|
};
|
||||||
use crate::services::share::csv::{CSVExport, CSVFormat};
|
use crate::services::share::csv::{CSVExport, CSVFormat};
|
||||||
use crate::services::sort::Sort;
|
use crate::services::sort::Sort;
|
||||||
@ -51,12 +53,6 @@ impl DatabaseEditor {
|
|||||||
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
let cell_cache = AnyTypeCache::<u64>::new();
|
let cell_cache = AnyTypeCache::<u64>::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();
|
let database_id = database.lock().get_database_id();
|
||||||
|
|
||||||
// Receive database sync state and send to frontend via the notification
|
// Receive database sync state and send to frontend via the notification
|
||||||
@ -93,8 +89,24 @@ impl DatabaseEditor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let database_views =
|
// Used to cache the view of the database for fast access.
|
||||||
Arc::new(DatabaseViews::new(database.clone(), cell_cache.clone(), database_view_data).await?);
|
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 {
|
Ok(Self {
|
||||||
database,
|
database,
|
||||||
cell_cache,
|
cell_cache,
|
||||||
@ -177,40 +189,9 @@ impl DatabaseEditor {
|
|||||||
Ok(self.database.lock().delete_view(view_id))
|
Ok(self.database.lock().delete_view(view_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_group_setting(
|
pub async fn update_group(&self, view_id: &str, changesets: GroupChangesets) -> FlowyResult<()> {
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
group_setting_changeset: GroupChangesets,
|
|
||||||
) -> FlowyResult<()> {
|
|
||||||
let view_editor = self.database_views.get_view_editor(view_id).await?;
|
let view_editor = self.database_views.get_view_editor(view_id).await?;
|
||||||
view_editor
|
view_editor.v_update_group(changesets).await?;
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,9 +276,7 @@ impl DatabaseEditor {
|
|||||||
.set_width_at_if_not_none(params.width.map(|value| value as i64))
|
.set_width_at_if_not_none(params.width.map(|value| value as i64))
|
||||||
.set_visibility_if_not_none(params.visibility);
|
.set_visibility_if_not_none(params.visibility);
|
||||||
});
|
});
|
||||||
self
|
notify_did_update_database_field(&self.database, ¶ms.field_id)?;
|
||||||
.notify_did_update_database_field(¶ms.field_id)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,30 +310,13 @@ impl DatabaseEditor {
|
|||||||
pub async fn update_field_type_option(
|
pub async fn update_field_type_option(
|
||||||
&self,
|
&self,
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
field_id: &str,
|
_field_id: &str,
|
||||||
type_option_data: TypeOptionData,
|
type_option_data: TypeOptionData,
|
||||||
old_field: Field,
|
old_field: Field,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
let field_type = FieldType::from(old_field.field_type);
|
let view_editor = self.database_views.get_view_editor(view_id).await?;
|
||||||
self
|
update_field_type_option_fn(&self.database, &view_editor, type_option_data, old_field).await?;
|
||||||
.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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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(())
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +397,7 @@ impl DatabaseEditor {
|
|||||||
let params = self.database.lock().duplicate_row(row_id);
|
let params = self.database.lock().duplicate_row(row_id);
|
||||||
match params {
|
match params {
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!("Failed to duplicate row: {}", row_id);
|
warn!("Failed to duplicate row: {}", row_id);
|
||||||
},
|
},
|
||||||
Some(params) => {
|
Some(params) => {
|
||||||
let _ = self.create_row(view_id, group_id, params).await;
|
let _ = self.create_row(view_id, group_id, params).await;
|
||||||
@ -585,7 +547,7 @@ impl DatabaseEditor {
|
|||||||
cover: row_meta.cover_url,
|
cover: row_meta.cover_url,
|
||||||
})
|
})
|
||||||
} else {
|
} 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
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,7 +556,7 @@ impl DatabaseEditor {
|
|||||||
if self.database.lock().views.is_row_exist(view_id, row_id) {
|
if self.database.lock().views.is_row_exist(view_id, row_id) {
|
||||||
self.database.lock().get_row_detail(row_id)
|
self.database.lock().get_row_detail(row_id)
|
||||||
} else {
|
} 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
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -984,7 +946,7 @@ impl DatabaseEditor {
|
|||||||
let row_detail = self.get_row_detail(view_id, &from_row);
|
let row_detail = self.get_row_detail(view_id, &from_row);
|
||||||
match row_detail {
|
match row_detail {
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(
|
warn!(
|
||||||
"Move row between group failed, can not find the row:{}",
|
"Move row between group failed, can not find the row:{}",
|
||||||
from_row
|
from_row
|
||||||
)
|
)
|
||||||
@ -1054,7 +1016,7 @@ impl DatabaseEditor {
|
|||||||
match self.database_views.get_view_editor(view_id).await {
|
match self.database_views.get_view_editor(view_id).await {
|
||||||
Ok(view) => view.v_get_all_calendar_events().await.unwrap_or_default(),
|
Ok(view) => view.v_get_all_calendar_events().await.unwrap_or_default(),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
tracing::warn!("Can not find the view: {}", view_id);
|
warn!("Can not find the view: {}", view_id);
|
||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1066,7 +1028,7 @@ impl DatabaseEditor {
|
|||||||
view_id: &str,
|
view_id: &str,
|
||||||
) -> FlowyResult<Vec<NoDateCalendarEventPB>> {
|
) -> FlowyResult<Vec<NoDateCalendarEventPB>> {
|
||||||
let _database_view = self.database_views.get_view_editor(view_id).await?;
|
let _database_view = self.database_views.get_view_editor(view_id).await?;
|
||||||
todo!()
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
@ -1084,28 +1046,6 @@ impl DatabaseEditor {
|
|||||||
Ok(())
|
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(
|
async fn notify_did_update_database(
|
||||||
&self,
|
&self,
|
||||||
changeset: DatabaseFieldChangesetPB,
|
changeset: DatabaseFieldChangesetPB,
|
||||||
@ -1134,7 +1074,7 @@ impl DatabaseEditor {
|
|||||||
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> {
|
pub async fn get_database_data(&self, view_id: &str) -> FlowyResult<DatabasePB> {
|
||||||
let database_view = self.database_views.get_view_editor(view_id).await?;
|
let database_view = self.database_views.get_view_editor(view_id).await?;
|
||||||
let view = database_view
|
let view = database_view
|
||||||
.get_view()
|
.v_get_view()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(FlowyError::record_not_found)?;
|
.ok_or_else(FlowyError::record_not_found)?;
|
||||||
let rows = database_view.v_get_rows().await;
|
let rows = database_view.v_get_rows().await;
|
||||||
@ -1284,13 +1224,14 @@ fn cell_changesets_from_cell_by_field_id(
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DatabaseViewDataImpl {
|
struct DatabaseViewOperationImpl {
|
||||||
database: Arc<MutexDatabase>,
|
database: Arc<MutexDatabase>,
|
||||||
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
|
editor_by_view_id: Arc<RwLock<EditorByViewId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseViewData for DatabaseViewDataImpl {
|
impl DatabaseViewOperation for DatabaseViewOperationImpl {
|
||||||
fn get_database(&self) -> Arc<MutexDatabase> {
|
fn get_database(&self) -> Arc<MutexDatabase> {
|
||||||
self.database.clone()
|
self.database.clone()
|
||||||
}
|
}
|
||||||
@ -1305,14 +1246,8 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
to_fut(async move { fields.into_iter().map(Arc::new).collect() })
|
to_fut(async move { fields.into_iter().map(Arc::new).collect() })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>> {
|
fn get_field(&self, field_id: &str) -> Option<Field> {
|
||||||
let field = self
|
self.database.lock().fields.get_field(field_id)
|
||||||
.database
|
|
||||||
.lock()
|
|
||||||
.fields
|
|
||||||
.get_field(field_id)
|
|
||||||
.map(Arc::new);
|
|
||||||
to_fut(async move { field })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_field(
|
fn create_field(
|
||||||
@ -1336,6 +1271,29 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
to_fut(async move { field })
|
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<Option<Arc<Field>>> {
|
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>> {
|
||||||
let field = self
|
let field = self
|
||||||
.database
|
.database
|
||||||
@ -1559,3 +1517,73 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
|||||||
.send()
|
.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||||
|
pub async fn update_field_type_option_fn(
|
||||||
|
database: &Arc<MutexDatabase>,
|
||||||
|
view_editor: &Arc<DatabaseViewEditor>,
|
||||||
|
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<MutexDatabase>,
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
pub use layout_deps::*;
|
pub use layout_deps::*;
|
||||||
pub use notifier::*;
|
pub use notifier::*;
|
||||||
pub use view_editor::*;
|
pub use view_editor::*;
|
||||||
|
pub use view_operation::*;
|
||||||
pub use views::*;
|
pub use views::*;
|
||||||
|
|
||||||
mod layout_deps;
|
mod layout_deps;
|
||||||
@ -8,6 +9,7 @@ mod notifier;
|
|||||||
mod view_editor;
|
mod view_editor;
|
||||||
mod view_filter;
|
mod view_filter;
|
||||||
mod view_group;
|
mod view_group;
|
||||||
|
mod view_operation;
|
||||||
mod view_sort;
|
mod view_sort;
|
||||||
mod views;
|
mod views;
|
||||||
// mod trait_impl;
|
// mod trait_impl;
|
||||||
|
@ -2,15 +2,13 @@ use std::borrow::Cow;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
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::fields::{Field, TypeOptionData};
|
||||||
use collab_database::rows::{Cells, Row, RowCell, RowDetail, RowId};
|
use collab_database::rows::{Cells, Row, RowDetail, RowId};
|
||||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
use collab_database::views::{DatabaseLayout, DatabaseView};
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_task::TaskDispatcher;
|
|
||||||
use lib_infra::future::Fut;
|
|
||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams,
|
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::{
|
use crate::services::database_view::view_group::{
|
||||||
get_cell_for_row, get_cells_for_field, new_group_controller, new_group_controller_with_field,
|
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::view_sort::make_sort_controller;
|
||||||
use crate::services::database_view::{
|
use crate::services::database_view::{
|
||||||
notify_did_update_filter, notify_did_update_group_rows, notify_did_update_num_of_groups,
|
notify_did_update_filter, notify_did_update_group_rows, notify_did_update_num_of_groups,
|
||||||
notify_did_update_setting, notify_did_update_sort, DatabaseLayoutDepsResolver,
|
notify_did_update_setting, notify_did_update_sort, DatabaseLayoutDepsResolver,
|
||||||
DatabaseViewChangedNotifier, DatabaseViewChangedReceiverRunner,
|
DatabaseViewChangedNotifier, DatabaseViewChangedReceiverRunner,
|
||||||
};
|
};
|
||||||
use crate::services::field::TypeOptionCellDataHandler;
|
|
||||||
use crate::services::field_settings::FieldSettings;
|
use crate::services::field_settings::FieldSettings;
|
||||||
use crate::services::filter::{
|
use crate::services::filter::{
|
||||||
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
|
Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
|
||||||
};
|
};
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
GroupChangeset, GroupChangesets, GroupController, GroupSetting, GroupSettingChangeset,
|
GroupChangesets, GroupController, GroupSetting, GroupSettingChangeset, MoveGroupRowContext,
|
||||||
MoveGroupRowContext, RowChangeset,
|
RowChangeset,
|
||||||
};
|
};
|
||||||
use crate::services::setting::CalendarLayoutSetting;
|
use crate::services::setting::CalendarLayoutSetting;
|
||||||
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
|
use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
|
||||||
|
|
||||||
pub trait DatabaseViewData: Send + Sync + 'static {
|
|
||||||
fn get_database(&self) -> Arc<MutexDatabase>;
|
|
||||||
|
|
||||||
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
|
|
||||||
/// If the field_ids is None, then it will return all the field revisions
|
|
||||||
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
|
||||||
|
|
||||||
/// Returns the field with the field_id
|
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
|
|
||||||
|
|
||||||
fn create_field(
|
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
name: &str,
|
|
||||||
field_type: FieldType,
|
|
||||||
type_option_data: TypeOptionData,
|
|
||||||
) -> Fut<Field>;
|
|
||||||
|
|
||||||
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>>;
|
|
||||||
|
|
||||||
/// Returns the index of the row with row_id
|
|
||||||
fn index_of_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<usize>>;
|
|
||||||
|
|
||||||
/// Returns the `index` and `RowRevision` with row_id
|
|
||||||
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
|
|
||||||
|
|
||||||
/// Returns all the rows in the view
|
|
||||||
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
|
||||||
|
|
||||||
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
|
|
||||||
|
|
||||||
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>>;
|
|
||||||
|
|
||||||
/// 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<GroupSetting>;
|
|
||||||
|
|
||||||
fn insert_group_setting(&self, view_id: &str, setting: GroupSetting);
|
|
||||||
|
|
||||||
fn get_sort(&self, view_id: &str, sort_id: &str) -> Option<Sort>;
|
|
||||||
|
|
||||||
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<Sort>;
|
|
||||||
|
|
||||||
fn remove_all_sorts(&self, view_id: &str);
|
|
||||||
|
|
||||||
fn get_all_filters(&self, view_id: &str) -> Vec<Arc<Filter>>;
|
|
||||||
|
|
||||||
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<Filter>;
|
|
||||||
|
|
||||||
fn get_filter_by_field_id(&self, view_id: &str, field_id: &str) -> Option<Filter>;
|
|
||||||
|
|
||||||
fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option<LayoutSetting>;
|
|
||||||
|
|
||||||
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<RwLock<TaskDispatcher>>;
|
|
||||||
|
|
||||||
fn get_type_option_cell_handler(
|
|
||||||
&self,
|
|
||||||
field: &Field,
|
|
||||||
field_type: &FieldType,
|
|
||||||
) -> Option<Box<dyn TypeOptionCellDataHandler>>;
|
|
||||||
|
|
||||||
fn get_field_settings(
|
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
field_ids: &[String],
|
|
||||||
) -> HashMap<String, FieldSettings>;
|
|
||||||
|
|
||||||
fn get_all_field_settings(&self, view_id: &str) -> HashMap<String, FieldSettings>;
|
|
||||||
|
|
||||||
fn update_field_settings(
|
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
field_id: &str,
|
|
||||||
visibility: Option<FieldVisibility>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DatabaseViewEditor {
|
pub struct DatabaseViewEditor {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
group_controller: Arc<RwLock<Option<Box<dyn GroupController>>>>,
|
group_controller: Arc<RwLock<Option<Box<dyn GroupController>>>>,
|
||||||
filter_controller: Arc<FilterController>,
|
filter_controller: Arc<FilterController>,
|
||||||
sort_controller: Arc<RwLock<SortController>>,
|
sort_controller: Arc<RwLock<SortController>>,
|
||||||
@ -158,14 +59,17 @@ impl Drop for DatabaseViewEditor {
|
|||||||
impl DatabaseViewEditor {
|
impl DatabaseViewEditor {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
view_id: String,
|
view_id: String,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
let (notifier, _) = broadcast::channel(100);
|
let (notifier, _) = broadcast::channel(100);
|
||||||
tokio::spawn(DatabaseViewChangedReceiverRunner(Some(notifier.subscribe())).run());
|
tokio::spawn(DatabaseViewChangedReceiverRunner(Some(notifier.subscribe())).run());
|
||||||
let group_controller = new_group_controller(view_id.clone(), delegate.clone()).await?;
|
// Group
|
||||||
let group_controller = Arc::new(RwLock::new(group_controller));
|
let group_controller = Arc::new(RwLock::new(
|
||||||
|
new_group_controller(view_id.clone(), delegate.clone()).await?,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Filter
|
||||||
let filter_controller = make_filter_controller(
|
let filter_controller = make_filter_controller(
|
||||||
&view_id,
|
&view_id,
|
||||||
delegate.clone(),
|
delegate.clone(),
|
||||||
@ -174,6 +78,7 @@ impl DatabaseViewEditor {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// Sort
|
||||||
let sort_controller = make_sort_controller(
|
let sort_controller = make_sort_controller(
|
||||||
&view_id,
|
&view_id,
|
||||||
delegate.clone(),
|
delegate.clone(),
|
||||||
@ -198,7 +103,7 @@ impl DatabaseViewEditor {
|
|||||||
self.filter_controller.close().await;
|
self.filter_controller.close().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_view(&self) -> Option<DatabaseView> {
|
pub async fn v_get_view(&self) -> Option<DatabaseView> {
|
||||||
self.delegate.get_view(&self.view_id).await
|
self.delegate.get_view(&self.view_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,7 +288,7 @@ impl DatabaseViewEditor {
|
|||||||
let move_row_context = MoveGroupRowContext {
|
let move_row_context = MoveGroupRowContext {
|
||||||
row_detail,
|
row_detail,
|
||||||
row_changeset,
|
row_changeset,
|
||||||
field: field.as_ref(),
|
field: &field,
|
||||||
to_group_id,
|
to_group_id,
|
||||||
to_row_id,
|
to_row_id,
|
||||||
};
|
};
|
||||||
@ -485,40 +390,31 @@ impl DatabaseViewEditor {
|
|||||||
Ok(result.flatten())
|
Ok(result.flatten())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn v_update_group_setting(&self, changeset: GroupChangesets) -> FlowyResult<()> {
|
pub async fn v_update_group(&self, changeset: GroupChangesets) -> FlowyResult<()> {
|
||||||
self
|
let mut type_option_data = TypeOptionData::new();
|
||||||
.mut_group_controller(|group_controller, _| {
|
let old_field = if let Some(controller) = self.group_controller.write().await.as_mut() {
|
||||||
group_controller.apply_group_setting_changeset(changeset)
|
let old_field = self.delegate.get_field(controller.field_id());
|
||||||
})
|
type_option_data.extend(controller.apply_group_changeset(&changeset).await?);
|
||||||
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn v_get_group_configuration_settings(&self) -> Vec<GroupSetting> {
|
pub async fn v_get_group_configuration_settings(&self) -> Vec<GroupSetting> {
|
||||||
self.delegate.get_group_setting(&self.view_id)
|
self.delegate.get_group_setting(&self.view_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_group(
|
|
||||||
&self,
|
|
||||||
changeset: GroupChangeset,
|
|
||||||
) -> FlowyResult<Option<TypeOptionData>> {
|
|
||||||
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<Sort> {
|
pub async fn v_get_all_sorts(&self) -> Vec<Sort> {
|
||||||
self.delegate.get_all_sorts(&self.view_id)
|
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) {
|
if let Some(value) = self.delegate.get_layout_setting(&self.view_id, layout_ty) {
|
||||||
let calendar_setting = CalendarLayoutSetting::from(value);
|
let calendar_setting = CalendarLayoutSetting::from(value);
|
||||||
// Check the field exist or not
|
// 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);
|
let field_type = FieldType::from(field.field_type);
|
||||||
|
|
||||||
// Check the type of field is Datetime or not
|
// 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<()> {
|
pub async fn v_set_layout_settings(&self, params: LayoutSettingParams) -> FlowyResult<()> {
|
||||||
// Maybe it needs no send notification to refresh the UI
|
// Maybe it needs no send notification to refresh the UI
|
||||||
if let Some(new_calendar_setting) = params.calendar {
|
if let Some(new_calendar_setting) = params.calendar {
|
||||||
if let Some(field) = self
|
if let Some(field) = self.delegate.get_field(&new_calendar_setting.field_id) {
|
||||||
.delegate
|
|
||||||
.get_field(&new_calendar_setting.field_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
let field_type = FieldType::from(field.field_type);
|
let field_type = FieldType::from(field.field_type);
|
||||||
if field_type != FieldType::DateTime {
|
if field_type != FieldType::DateTime {
|
||||||
return Err(FlowyError::unexpect_calendar_field_type());
|
return Err(FlowyError::unexpect_calendar_field_type());
|
||||||
@ -729,13 +621,19 @@ impl DatabaseViewEditor {
|
|||||||
Ok(())
|
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)]
|
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||||
pub async fn v_did_update_field_type_option(
|
pub async fn v_did_update_field_type_option(&self, old_field: &Field) -> FlowyResult<()> {
|
||||||
&self,
|
let field_id = &old_field.id;
|
||||||
field_id: &str,
|
// If the id of the grouping field is equal to the updated field's id, then we need to
|
||||||
old_field: &Field,
|
// update the group setting
|
||||||
) -> FlowyResult<()> {
|
if self.is_grouping_field(field_id).await {
|
||||||
if let Some(field) = self.delegate.get_field(field_id).await {
|
self.v_grouping_by_field(field_id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(field) = self.delegate.get_field(field_id) {
|
||||||
self
|
self
|
||||||
.sort_controller
|
.sort_controller
|
||||||
.read()
|
.read()
|
||||||
@ -776,9 +674,13 @@ impl DatabaseViewEditor {
|
|||||||
/// Called when a grouping field is updated.
|
/// Called when a grouping field is updated.
|
||||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn v_grouping_by_field(&self, field_id: &str) -> FlowyResult<()> {
|
pub async fn v_grouping_by_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||||
if let Some(field) = self.delegate.get_field(field_id).await {
|
if let Some(field) = self.delegate.get_field(field_id) {
|
||||||
let new_group_controller =
|
let new_group_controller = new_group_controller_with_field(
|
||||||
new_group_controller_with_field(self.view_id.clone(), self.delegate.clone(), field).await?;
|
self.view_id.clone(),
|
||||||
|
self.delegate.clone(),
|
||||||
|
Arc::new(field),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let new_groups = new_group_controller
|
let new_groups = new_group_controller
|
||||||
.groups()
|
.groups()
|
||||||
@ -812,7 +714,7 @@ impl DatabaseViewEditor {
|
|||||||
let text_cell = get_cell_for_row(self.delegate.clone(), &primary_field.id, &row_id).await?;
|
let text_cell = get_cell_for_row(self.delegate.clone(), &primary_field.id, &row_id).await?;
|
||||||
|
|
||||||
// Date
|
// 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 date_cell = get_cell_for_row(self.delegate.clone(), &date_field.id, &row_id).await?;
|
||||||
let title = text_cell
|
let title = text_cell
|
||||||
@ -973,7 +875,7 @@ impl DatabaseViewEditor {
|
|||||||
|
|
||||||
async fn mut_group_controller<F, T>(&self, f: F) -> Option<T>
|
async fn mut_group_controller<F, T>(&self, f: F) -> Option<T>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Box<dyn GroupController>, Arc<Field>) -> FlowyResult<T>,
|
F: FnOnce(&mut Box<dyn GroupController>, Field) -> FlowyResult<T>,
|
||||||
{
|
{
|
||||||
let group_field_id = self
|
let group_field_id = self
|
||||||
.group_controller
|
.group_controller
|
||||||
@ -981,8 +883,7 @@ impl DatabaseViewEditor {
|
|||||||
.await
|
.await
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|group| group.field_id().to_owned())?;
|
.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;
|
let mut write_guard = self.group_controller.write().await;
|
||||||
if let Some(group_controller) = &mut *write_guard {
|
if let Some(group_controller) = &mut *write_guard {
|
||||||
f(group_controller, field).ok()
|
f(group_controller, field).ok()
|
||||||
|
@ -7,13 +7,13 @@ use lib_infra::future::{to_fut, Fut};
|
|||||||
|
|
||||||
use crate::services::cell::CellCache;
|
use crate::services::cell::CellCache;
|
||||||
use crate::services::database_view::{
|
use crate::services::database_view::{
|
||||||
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData,
|
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewOperation,
|
||||||
};
|
};
|
||||||
use crate::services::filter::{Filter, FilterController, FilterDelegate, FilterTaskHandler};
|
use crate::services::filter::{Filter, FilterController, FilterDelegate, FilterTaskHandler};
|
||||||
|
|
||||||
pub async fn make_filter_controller(
|
pub async fn make_filter_controller(
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
notifier: DatabaseViewChangedNotifier,
|
notifier: DatabaseViewChangedNotifier,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
) -> Arc<FilterController> {
|
) -> Arc<FilterController> {
|
||||||
@ -43,7 +43,7 @@ pub async fn make_filter_controller(
|
|||||||
filter_controller
|
filter_controller
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DatabaseViewFilterDelegateImpl(Arc<dyn DatabaseViewData>);
|
struct DatabaseViewFilterDelegateImpl(Arc<dyn DatabaseViewOperation>);
|
||||||
|
|
||||||
impl FilterDelegate for DatabaseViewFilterDelegateImpl {
|
impl FilterDelegate for DatabaseViewFilterDelegateImpl {
|
||||||
fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut<Option<Arc<Filter>>> {
|
fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut<Option<Arc<Filter>>> {
|
||||||
@ -51,7 +51,7 @@ impl FilterDelegate for DatabaseViewFilterDelegateImpl {
|
|||||||
to_fut(async move { filter })
|
to_fut(async move { filter })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>> {
|
fn get_field(&self, field_id: &str) -> Option<Field> {
|
||||||
self.0.get_field(field_id)
|
self.0.get_field(field_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,40 +1,43 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::{Cell, RowId};
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use lib_infra::future::{to_fut, Fut};
|
use lib_infra::future::{to_fut, Fut};
|
||||||
|
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
use crate::services::database_view::DatabaseViewData;
|
use crate::services::database_view::DatabaseViewOperation;
|
||||||
use crate::services::field::RowSingleCellData;
|
use crate::services::field::RowSingleCellData;
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
find_new_grouping_field, make_group_controller, GroupController, GroupSetting,
|
find_new_grouping_field, make_group_controller, GroupController, GroupSetting,
|
||||||
GroupSettingReader, GroupSettingWriter,
|
GroupSettingReader, GroupSettingWriter, GroupTypeOptionCellOperation,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn new_group_controller_with_field(
|
pub async fn new_group_controller_with_field(
|
||||||
view_id: String,
|
view_id: String,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
grouping_field: Arc<Field>,
|
grouping_field: Arc<Field>,
|
||||||
) -> FlowyResult<Box<dyn GroupController>> {
|
) -> FlowyResult<Box<dyn GroupController>> {
|
||||||
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
||||||
let rows = delegate.get_rows(&view_id).await;
|
let rows = delegate.get_rows(&view_id).await;
|
||||||
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
||||||
|
let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone());
|
||||||
make_group_controller(
|
make_group_controller(
|
||||||
view_id,
|
view_id,
|
||||||
grouping_field,
|
grouping_field,
|
||||||
rows,
|
rows,
|
||||||
setting_reader,
|
setting_reader,
|
||||||
setting_writer,
|
setting_writer,
|
||||||
|
type_option_writer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_group_controller(
|
pub async fn new_group_controller(
|
||||||
view_id: String,
|
view_id: String,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
) -> FlowyResult<Option<Box<dyn GroupController>>> {
|
) -> FlowyResult<Option<Box<dyn GroupController>>> {
|
||||||
let fields = delegate.get_fields(&view_id, None).await;
|
let fields = delegate.get_fields(&view_id, None).await;
|
||||||
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
||||||
@ -59,6 +62,7 @@ pub async fn new_group_controller(
|
|||||||
if let Some(grouping_field) = grouping_field {
|
if let Some(grouping_field) = grouping_field {
|
||||||
let rows = delegate.get_rows(&view_id).await;
|
let rows = delegate.get_rows(&view_id).await;
|
||||||
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
||||||
|
let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone());
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
make_group_controller(
|
make_group_controller(
|
||||||
view_id,
|
view_id,
|
||||||
@ -66,6 +70,7 @@ pub async fn new_group_controller(
|
|||||||
rows,
|
rows,
|
||||||
setting_reader,
|
setting_reader,
|
||||||
setting_writer,
|
setting_writer,
|
||||||
|
type_option_writer,
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
))
|
))
|
||||||
@ -74,7 +79,7 @@ pub async fn new_group_controller(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct GroupSettingReaderImpl(pub Arc<dyn DatabaseViewData>);
|
pub(crate) struct GroupSettingReaderImpl(pub Arc<dyn DatabaseViewOperation>);
|
||||||
|
|
||||||
impl GroupSettingReader for GroupSettingReaderImpl {
|
impl GroupSettingReader for GroupSettingReaderImpl {
|
||||||
fn get_group_setting(&self, view_id: &str) -> Fut<Option<Arc<GroupSetting>>> {
|
fn get_group_setting(&self, view_id: &str) -> Fut<Option<Arc<GroupSetting>>> {
|
||||||
@ -97,11 +102,11 @@ impl GroupSettingReader for GroupSettingReaderImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_cell_for_row(
|
pub(crate) async fn get_cell_for_row(
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
field_id: &str,
|
field_id: &str,
|
||||||
row_id: &RowId,
|
row_id: &RowId,
|
||||||
) -> Option<RowSingleCellData> {
|
) -> Option<RowSingleCellData> {
|
||||||
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 row_cell = delegate.get_cell_in_row(field_id, row_id).await;
|
||||||
let field_type = FieldType::from(field.field_type);
|
let field_type = FieldType::from(field.field_type);
|
||||||
let handler = delegate.get_type_option_cell_handler(&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.
|
// Returns the list of cells corresponding to the given field.
|
||||||
pub(crate) async fn get_cells_for_field(
|
pub(crate) async fn get_cells_for_field(
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
field_id: &str,
|
field_id: &str,
|
||||||
) -> Vec<RowSingleCellData> {
|
) -> Vec<RowSingleCellData> {
|
||||||
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);
|
let field_type = FieldType::from(field.field_type);
|
||||||
if let Some(handler) = delegate.get_type_option_cell_handler(&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;
|
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![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GroupSettingWriterImpl(Arc<dyn DatabaseViewData>);
|
struct GroupSettingWriterImpl(Arc<dyn DatabaseViewOperation>);
|
||||||
|
|
||||||
impl GroupSettingWriter for GroupSettingWriterImpl {
|
impl GroupSettingWriter for GroupSettingWriterImpl {
|
||||||
fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut<FlowyResult<()>> {
|
fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut<FlowyResult<()>> {
|
||||||
self.0.insert_group_setting(view_id, group_setting);
|
self.0.insert_group_setting(view_id, group_setting);
|
||||||
to_fut(async move { Ok(()) })
|
to_fut(async move { Ok(()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GroupTypeOptionCellWriterImpl(Arc<dyn DatabaseViewOperation>);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl GroupTypeOptionCellOperation for GroupTypeOptionCellWriterImpl {
|
||||||
|
async fn get_cell(&self, _row_id: &RowId, _field_id: &str) -> FlowyResult<Option<Cell>> {
|
||||||
|
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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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<MutexDatabase>;
|
||||||
|
|
||||||
|
/// Get the view of the database with the view_id
|
||||||
|
fn get_view(&self, view_id: &str) -> Fut<Option<DatabaseView>>;
|
||||||
|
/// If the field_ids is None, then it will return all the field revisions
|
||||||
|
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
||||||
|
|
||||||
|
/// Returns the field with the field_id
|
||||||
|
fn get_field(&self, field_id: &str) -> Option<Field>;
|
||||||
|
|
||||||
|
fn create_field(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
name: &str,
|
||||||
|
field_type: FieldType,
|
||||||
|
type_option_data: TypeOptionData,
|
||||||
|
) -> Fut<Field>;
|
||||||
|
|
||||||
|
fn update_field(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
type_option_data: TypeOptionData,
|
||||||
|
old_field: Field,
|
||||||
|
) -> FutureResult<(), FlowyError>;
|
||||||
|
|
||||||
|
fn get_primary_field(&self) -> Fut<Option<Arc<Field>>>;
|
||||||
|
|
||||||
|
/// Returns the index of the row with row_id
|
||||||
|
fn index_of_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<usize>>;
|
||||||
|
|
||||||
|
/// Returns the `index` and `RowRevision` with row_id
|
||||||
|
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
|
||||||
|
|
||||||
|
/// Returns all the rows in the view
|
||||||
|
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
||||||
|
|
||||||
|
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
|
||||||
|
|
||||||
|
fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>>;
|
||||||
|
|
||||||
|
/// 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<GroupSetting>;
|
||||||
|
|
||||||
|
fn insert_group_setting(&self, view_id: &str, setting: GroupSetting);
|
||||||
|
|
||||||
|
fn get_sort(&self, view_id: &str, sort_id: &str) -> Option<Sort>;
|
||||||
|
|
||||||
|
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<Sort>;
|
||||||
|
|
||||||
|
fn remove_all_sorts(&self, view_id: &str);
|
||||||
|
|
||||||
|
fn get_all_filters(&self, view_id: &str) -> Vec<Arc<Filter>>;
|
||||||
|
|
||||||
|
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<Filter>;
|
||||||
|
|
||||||
|
fn get_filter_by_field_id(&self, view_id: &str, field_id: &str) -> Option<Filter>;
|
||||||
|
|
||||||
|
fn get_layout_setting(&self, view_id: &str, layout_ty: &DatabaseLayout) -> Option<LayoutSetting>;
|
||||||
|
|
||||||
|
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<RwLock<TaskDispatcher>>;
|
||||||
|
|
||||||
|
fn get_type_option_cell_handler(
|
||||||
|
&self,
|
||||||
|
field: &Field,
|
||||||
|
field_type: &FieldType,
|
||||||
|
) -> Option<Box<dyn TypeOptionCellDataHandler>>;
|
||||||
|
|
||||||
|
fn get_field_settings(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
field_ids: &[String],
|
||||||
|
) -> HashMap<String, FieldSettings>;
|
||||||
|
|
||||||
|
fn get_all_field_settings(&self, view_id: &str) -> HashMap<String, FieldSettings>;
|
||||||
|
|
||||||
|
fn update_field_settings(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
field_id: &str,
|
||||||
|
visibility: Option<FieldVisibility>,
|
||||||
|
);
|
||||||
|
}
|
@ -8,14 +8,14 @@ use lib_infra::future::{to_fut, Fut};
|
|||||||
|
|
||||||
use crate::services::cell::CellCache;
|
use crate::services::cell::CellCache;
|
||||||
use crate::services::database_view::{
|
use crate::services::database_view::{
|
||||||
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData,
|
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewOperation,
|
||||||
};
|
};
|
||||||
use crate::services::filter::FilterController;
|
use crate::services::filter::FilterController;
|
||||||
use crate::services::sort::{Sort, SortController, SortDelegate, SortTaskHandler};
|
use crate::services::sort::{Sort, SortController, SortDelegate, SortTaskHandler};
|
||||||
|
|
||||||
pub(crate) async fn make_sort_controller(
|
pub(crate) async fn make_sort_controller(
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
notifier: DatabaseViewChangedNotifier,
|
notifier: DatabaseViewChangedNotifier,
|
||||||
filter_controller: Arc<FilterController>,
|
filter_controller: Arc<FilterController>,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
@ -49,7 +49,7 @@ pub(crate) async fn make_sort_controller(
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DatabaseViewSortDelegateImpl {
|
struct DatabaseViewSortDelegateImpl {
|
||||||
delegate: Arc<dyn DatabaseViewData>,
|
delegate: Arc<dyn DatabaseViewOperation>,
|
||||||
filter_controller: Arc<FilterController>,
|
filter_controller: Arc<FilterController>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ impl SortDelegate for DatabaseViewSortDelegateImpl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>> {
|
fn get_field(&self, field_id: &str) -> Option<Field> {
|
||||||
self.delegate.get_field(field_id)
|
self.delegate.get_field(field_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,47 +2,46 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab_database::database::MutexDatabase;
|
use collab_database::database::MutexDatabase;
|
||||||
use collab_database::fields::Field;
|
|
||||||
use collab_database::rows::{RowDetail, RowId};
|
use collab_database::rows::{RowDetail, RowId};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use lib_infra::future::Fut;
|
use lib_infra::future::Fut;
|
||||||
|
|
||||||
use crate::services::cell::CellCache;
|
use crate::services::cell::CellCache;
|
||||||
use crate::services::database::DatabaseRowEvent;
|
use crate::services::database::DatabaseRowEvent;
|
||||||
use crate::services::database_view::{DatabaseViewData, DatabaseViewEditor};
|
use crate::services::database_view::{DatabaseViewEditor, DatabaseViewOperation};
|
||||||
use crate::services::group::RowChangeset;
|
use crate::services::group::RowChangeset;
|
||||||
|
|
||||||
pub type RowEventSender = broadcast::Sender<DatabaseRowEvent>;
|
pub type RowEventSender = broadcast::Sender<DatabaseRowEvent>;
|
||||||
pub type RowEventReceiver = broadcast::Receiver<DatabaseRowEvent>;
|
pub type RowEventReceiver = broadcast::Receiver<DatabaseRowEvent>;
|
||||||
|
pub type EditorByViewId = HashMap<String, Arc<DatabaseViewEditor>>;
|
||||||
pub struct DatabaseViews {
|
pub struct DatabaseViews {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
database: Arc<MutexDatabase>,
|
database: Arc<MutexDatabase>,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
database_view_data: Arc<dyn DatabaseViewData>,
|
view_operation: Arc<dyn DatabaseViewOperation>,
|
||||||
editor_map: Arc<RwLock<HashMap<String, Arc<DatabaseViewEditor>>>>,
|
editor_by_view_id: Arc<RwLock<EditorByViewId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseViews {
|
impl DatabaseViews {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
database: Arc<MutexDatabase>,
|
database: Arc<MutexDatabase>,
|
||||||
cell_cache: CellCache,
|
cell_cache: CellCache,
|
||||||
database_view_data: Arc<dyn DatabaseViewData>,
|
view_operation: Arc<dyn DatabaseViewOperation>,
|
||||||
|
editor_by_view_id: Arc<RwLock<EditorByViewId>>,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
let editor_map = Arc::new(RwLock::new(HashMap::default()));
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
database,
|
database,
|
||||||
database_view_data,
|
view_operation,
|
||||||
cell_cache,
|
cell_cache,
|
||||||
editor_map,
|
editor_by_view_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn close_view(&self, view_id: &str) -> bool {
|
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) {
|
if let Some(view) = editor_map.remove(view_id) {
|
||||||
view.close().await;
|
view.close().await;
|
||||||
}
|
}
|
||||||
@ -50,7 +49,13 @@ impl DatabaseViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn editors(&self) -> Vec<Arc<DatabaseViewEditor>> {
|
pub async fn editors(&self) -> Vec<Arc<DatabaseViewEditor>> {
|
||||||
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.
|
/// It may generate a RowChangeset when the Row was moved from one group to another.
|
||||||
@ -77,43 +82,22 @@ impl DatabaseViews {
|
|||||||
Ok(())
|
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<Arc<DatabaseViewEditor>> {
|
pub async fn get_view_editor(&self, view_id: &str) -> FlowyResult<Arc<DatabaseViewEditor>> {
|
||||||
debug_assert!(!view_id.is_empty());
|
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());
|
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(
|
let editor = Arc::new(
|
||||||
DatabaseViewEditor::new(
|
DatabaseViewEditor::new(
|
||||||
view_id.to_owned(),
|
view_id.to_owned(),
|
||||||
self.database_view_data.clone(),
|
self.view_operation.clone(),
|
||||||
self.cell_cache.clone(),
|
self.cell_cache.clone(),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
@ -24,7 +24,6 @@ impl SelectOptionIds {
|
|||||||
pub fn into_inner(self) -> Vec<String> {
|
pub fn into_inner(self) -> Vec<String> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_cell_data(&self, field_type: FieldType) -> Cell {
|
pub fn to_cell_data(&self, field_type: FieldType) -> Cell {
|
||||||
new_cell_builder(field_type)
|
new_cell_builder(field_type)
|
||||||
.insert_str_value(CELL_DATA, self.to_string())
|
.insert_str_value(CELL_DATA, self.to_string())
|
||||||
|
@ -33,7 +33,15 @@ pub trait TypeOption {
|
|||||||
///
|
///
|
||||||
/// Uses `StrCellData` for any `TypeOption` if their cell data is pure `String`.
|
/// 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.
|
/// Represents as the corresponding field type cell changeset.
|
||||||
/// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.
|
/// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.
|
||||||
|
@ -113,16 +113,21 @@ where
|
|||||||
+ Sync
|
+ Sync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
pub fn into_boxed(self) -> Box<dyn TypeOptionCellDataHandler> {
|
||||||
|
Box::new(self) as Box<dyn TypeOptionCellDataHandler>
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_with_boxed(
|
pub fn new_with_boxed(
|
||||||
inner: T,
|
inner: T,
|
||||||
cell_filter_cache: Option<CellFilterCache>,
|
cell_filter_cache: Option<CellFilterCache>,
|
||||||
cell_data_cache: Option<CellCache>,
|
cell_data_cache: Option<CellCache>,
|
||||||
) -> Box<dyn TypeOptionCellDataHandler> {
|
) -> Box<dyn TypeOptionCellDataHandler> {
|
||||||
Box::new(Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
cell_data_cache,
|
cell_data_cache,
|
||||||
cell_filter_cache,
|
cell_filter_cache,
|
||||||
}) as Box<dyn TypeOptionCellDataHandler>
|
}
|
||||||
|
.into_boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ use crate::services::filter::{Filter, FilterChangeset, FilterResult, FilterResul
|
|||||||
|
|
||||||
pub trait FilterDelegate: Send + Sync + 'static {
|
pub trait FilterDelegate: Send + Sync + 'static {
|
||||||
fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut<Option<Arc<Filter>>>;
|
fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut<Option<Arc<Filter>>>;
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
|
fn get_field(&self, field_id: &str) -> Option<Field>;
|
||||||
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
||||||
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
||||||
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
|
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
|
||||||
|
@ -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 collab_database::rows::{Cell, Row, RowDetail};
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
|
|
||||||
use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
|
use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
|
||||||
use crate::services::cell::DecodedCellData;
|
use crate::services::field::TypeOption;
|
||||||
use crate::services::group::controller::MoveGroupRowContext;
|
|
||||||
use crate::services::group::entities::GroupSetting;
|
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.
|
/// Using polymorphism to provides the customs action for different group controller.
|
||||||
///
|
///
|
||||||
/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
|
/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
|
||||||
///
|
///
|
||||||
pub trait GroupCustomize: Send + Sync {
|
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.
|
/// Returns the a value of the cell if the cell data is not exist.
|
||||||
/// The default value is `None`
|
/// 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.
|
/// 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: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
fn create_or_delete_group_when_cell_changed(
|
fn create_or_delete_group_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
_row_detail: &RowDetail,
|
_row_detail: &RowDetail,
|
||||||
_old_cell_data: Option<&Self::CellData>,
|
_old_cell_data: Option<&<Self::GroupTypeOption as TypeOption>::CellProtobufType>,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||||
Ok((None, None))
|
Ok((None, None))
|
||||||
}
|
}
|
||||||
@ -43,16 +49,20 @@ pub trait GroupCustomize: Send + Sync {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB>;
|
) -> Vec<GroupRowsNotificationPB>;
|
||||||
|
|
||||||
/// Deletes the row from the group
|
/// Deletes the row from the group
|
||||||
fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB>;
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB>;
|
||||||
|
|
||||||
/// Move row from one group to another
|
/// Move row from one group to another
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
context: MoveGroupRowContext,
|
context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB>;
|
) -> Vec<GroupRowsNotificationPB>;
|
||||||
|
|
||||||
@ -60,13 +70,14 @@ pub trait GroupCustomize: Send + Sync {
|
|||||||
fn delete_group_when_move_row(
|
fn delete_group_when_move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_row: &Row,
|
_row: &Row,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Option<GroupPB> {
|
) -> Option<GroupPB> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the shared actions any group controller can perform.
|
/// Defines the shared actions any group controller can perform.
|
||||||
|
#[async_trait]
|
||||||
pub trait GroupControllerOperation: Send + Sync {
|
pub trait GroupControllerOperation: Send + Sync {
|
||||||
/// The field that is used for grouping the rows
|
/// The field that is used for grouping the rows
|
||||||
fn field_id(&self) -> &str;
|
fn field_id(&self) -> &str;
|
||||||
@ -104,7 +115,10 @@ pub trait GroupControllerOperation: Send + Sync {
|
|||||||
/// Update the group if the corresponding field is changed
|
/// Update the group if the corresponding field is changed
|
||||||
fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesPB>>;
|
fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesPB>>;
|
||||||
|
|
||||||
fn apply_group_setting_changeset(&mut self, changeset: GroupChangesets) -> FlowyResult<()>;
|
async fn apply_group_changeset(
|
||||||
|
&mut self,
|
||||||
|
changeset: &GroupChangesets,
|
||||||
|
) -> FlowyResult<TypeOptionData>;
|
||||||
|
|
||||||
fn apply_group_configuration_setting_changeset(
|
fn apply_group_configuration_setting_changeset(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -3,10 +3,13 @@ use std::fmt::Formatter;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
|
use collab_database::rows::{Cell, RowId};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use tracing::event;
|
||||||
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use lib_infra::future::Fut;
|
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<FlowyResult<()>>;
|
fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut<FlowyResult<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait GroupTypeOptionCellOperation: Send + Sync + 'static {
|
||||||
|
async fn get_cell(&self, row_id: &RowId, field_id: &str) -> FlowyResult<Option<Cell>>;
|
||||||
|
async fn update_cell(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
row_id: &RowId,
|
||||||
|
field_id: &str,
|
||||||
|
cell: Cell,
|
||||||
|
) -> FlowyResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> std::fmt::Display for GroupContext<T> {
|
impl<T> std::fmt::Display for GroupContext<T> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
self.group_by_id.iter().for_each(|(_, group)| {
|
self.group_by_id.iter().for_each(|(_, group)| {
|
||||||
@ -64,7 +79,6 @@ pub struct GroupContext<C> {
|
|||||||
|
|
||||||
/// A reader that implement the [GroupSettingReader] trait
|
/// A reader that implement the [GroupSettingReader] trait
|
||||||
///
|
///
|
||||||
#[allow(dead_code)]
|
|
||||||
reader: Arc<dyn GroupSettingReader>,
|
reader: Arc<dyn GroupSettingReader>,
|
||||||
|
|
||||||
/// A writer that implement the [GroupSettingWriter] trait is used to save the
|
/// A writer that implement the [GroupSettingWriter] trait is used to save the
|
||||||
@ -84,6 +98,7 @@ where
|
|||||||
reader: Arc<dyn GroupSettingReader>,
|
reader: Arc<dyn GroupSettingReader>,
|
||||||
writer: Arc<dyn GroupSettingWriter>,
|
writer: Arc<dyn GroupSettingWriter>,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
|
event!(tracing::Level::TRACE, "GroupContext::new");
|
||||||
let setting = match reader.get_group_setting(&view_id).await {
|
let setting = match reader.get_group_setting(&view_id).await {
|
||||||
None => {
|
None => {
|
||||||
let default_configuration = default_group_setting(&field);
|
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| {
|
let update_group = self.mut_group(&group_changeset.group_id, |group| {
|
||||||
if let Some(visible) = group_changeset.visible {
|
if let Some(visible) = group_changeset.visible {
|
||||||
group.visible = visible;
|
group.visible = visible;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use collab_database::fields::{Field, TypeOptionData};
|
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::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@ -12,13 +13,16 @@ use flowy_error::FlowyResult;
|
|||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB,
|
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::{
|
use crate::services::group::action::{
|
||||||
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize,
|
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize,
|
||||||
};
|
};
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::entities::{GroupData, GroupSetting};
|
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;
|
// use collab_database::views::Group;
|
||||||
|
|
||||||
@ -32,108 +36,67 @@ use crate::services::group::{Group, GroupChangesets, GroupSettingChangeset};
|
|||||||
///
|
///
|
||||||
pub trait GroupController: GroupControllerOperation + Send + Sync {
|
pub trait GroupController: GroupControllerOperation + Send + Sync {
|
||||||
/// Called when the type option of the [Field] was updated.
|
/// Called when the type option of the [Field] was updated.
|
||||||
fn did_update_field_type_option(&mut self, field: &Arc<Field>);
|
fn did_update_field_type_option(&mut self, field: &Field);
|
||||||
|
|
||||||
/// Called before the row was created.
|
/// Called before the row was created.
|
||||||
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str);
|
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str);
|
||||||
|
|
||||||
/// Called after the row was created.
|
/// Called after the row was created.
|
||||||
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str);
|
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<TypeOptionData> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [GroupsBuilder] trait is used to generate the groups for different [FieldType]
|
#[async_trait]
|
||||||
pub trait GroupsBuilder {
|
pub trait GroupOperationInterceptor {
|
||||||
type Context;
|
type GroupTypeOption: TypeOption;
|
||||||
type TypeOptionType;
|
async fn type_option_from_group_changeset(
|
||||||
|
&self,
|
||||||
fn build(
|
changeset: &GroupChangeset,
|
||||||
field: &Field,
|
type_option: &Self::GroupTypeOption,
|
||||||
context: &Self::Context,
|
view_id: &str,
|
||||||
type_option: &Option<Self::TypeOptionType>,
|
) -> Option<TypeOptionData>;
|
||||||
) -> GeneratedGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GeneratedGroups {
|
|
||||||
pub no_status_group: Option<Group>,
|
|
||||||
pub group_configs: Vec<GeneratedGroupConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<RowId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RowChangeset {
|
|
||||||
pub row_id: RowId,
|
|
||||||
pub height: Option<i32>,
|
|
||||||
pub visibility: Option<bool>,
|
|
||||||
// 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<String, Cell>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C: represents the group configuration that impl [GroupConfigurationSerde]
|
/// C: represents the group configuration that impl [GroupConfigurationSerde]
|
||||||
/// T: the type-option data deserializer that impl [TypeOptionDataDeserializer]
|
/// T: the type-option data deserializer that impl [TypeOptionDataDeserializer]
|
||||||
/// G: the group generator, [GroupsBuilder]
|
/// G: the group generator, [GroupsBuilder]
|
||||||
/// P: the parser that impl [CellProtobufBlobParser] for the CellBytes
|
/// P: the parser that impl [CellProtobufBlobParser] for the CellBytes
|
||||||
pub struct BaseGroupController<C, T, G, P> {
|
pub struct BaseGroupController<C, T, G, P, I> {
|
||||||
pub grouping_field_id: String,
|
pub grouping_field_id: String,
|
||||||
pub type_option: Option<T>,
|
pub type_option: T,
|
||||||
pub context: GroupContext<C>,
|
pub context: GroupContext<C>,
|
||||||
group_action_phantom: PhantomData<G>,
|
group_builder_phantom: PhantomData<G>,
|
||||||
cell_parser_phantom: PhantomData<P>,
|
cell_parser_phantom: PhantomData<P>,
|
||||||
|
pub operation_interceptor: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, T, G, P> BaseGroupController<C, T, G, P>
|
impl<C, T, G, P, I> BaseGroupController<C, T, G, P, I>
|
||||||
where
|
where
|
||||||
C: Serialize + DeserializeOwned,
|
C: Serialize + DeserializeOwned,
|
||||||
T: From<TypeOptionData>,
|
T: TypeOption + From<TypeOptionData> + Send + Sync,
|
||||||
G: GroupsBuilder<Context = GroupContext<C>, TypeOptionType = T>,
|
G: GroupsBuilder<Context = GroupContext<C>, GroupTypeOption = T>,
|
||||||
|
I: GroupOperationInterceptor<GroupTypeOption = T> + Send + Sync,
|
||||||
{
|
{
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
grouping_field: &Arc<Field>,
|
grouping_field: &Arc<Field>,
|
||||||
mut configuration: GroupContext<C>,
|
mut configuration: GroupContext<C>,
|
||||||
|
operation_interceptor: I,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
let field_type = FieldType::from(grouping_field.field_type);
|
let field_type = FieldType::from(grouping_field.field_type);
|
||||||
let type_option = grouping_field.get_type_option::<T>(field_type);
|
let type_option = grouping_field
|
||||||
let generated_groups = G::build(grouping_field, &configuration, &type_option);
|
.get_type_option::<T>(&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)?;
|
let _ = configuration.init_groups(generated_groups)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
grouping_field_id: grouping_field.id.clone(),
|
grouping_field_id: grouping_field.id.clone(),
|
||||||
type_option,
|
type_option,
|
||||||
context: configuration,
|
context: configuration,
|
||||||
group_action_phantom: PhantomData,
|
group_builder_phantom: PhantomData,
|
||||||
cell_parser_phantom: PhantomData,
|
cell_parser_phantom: PhantomData,
|
||||||
|
operation_interceptor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,14 +172,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, T, G, P> GroupControllerOperation for BaseGroupController<C, T, G, P>
|
#[async_trait]
|
||||||
|
impl<C, T, G, P, I> GroupControllerOperation for BaseGroupController<C, T, G, P, I>
|
||||||
where
|
where
|
||||||
P: CellProtobufBlobParser,
|
P: CellProtobufBlobParser<Object = <T as TypeOption>::CellProtobufType>,
|
||||||
C: Serialize + DeserializeOwned,
|
C: Serialize + DeserializeOwned + Sync + Send,
|
||||||
T: From<TypeOptionData>,
|
T: TypeOption + From<TypeOptionData> + Send + Sync,
|
||||||
G: GroupsBuilder<Context = GroupContext<C>, TypeOptionType = T>,
|
G: GroupsBuilder<Context = GroupContext<C>, GroupTypeOption = T>,
|
||||||
|
I: GroupOperationInterceptor<GroupTypeOption = T> + Send + Sync,
|
||||||
Self: GroupCustomize<CellData = P::Object>,
|
Self: GroupCustomize<GroupTypeOption = T>,
|
||||||
{
|
{
|
||||||
fn field_id(&self) -> &str {
|
fn field_id(&self) -> &str {
|
||||||
&self.grouping_field_id
|
&self.grouping_field_id
|
||||||
@ -232,7 +196,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all, fields(row_count=%rows.len(), group_result))]
|
#[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 {
|
for row_detail in rows {
|
||||||
let cell = match row_detail.row.cells.get(&self.grouping_field_id) {
|
let cell = match row_detail.row.cells.get(&self.grouping_field_id) {
|
||||||
None => self.placeholder_cell(),
|
None => self.placeholder_cell(),
|
||||||
@ -241,8 +205,7 @@ where
|
|||||||
|
|
||||||
if let Some(cell) = cell {
|
if let Some(cell) = cell {
|
||||||
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
||||||
let cell_bytes = get_cell_protobuf(&cell, field, None);
|
let cell_data = <T as TypeOption>::CellData::from(&cell);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
|
||||||
for group in self.context.groups() {
|
for group in self.context.groups() {
|
||||||
if self.can_group(&group.filter_content, &cell_data) {
|
if self.can_group(&group.filter_content, &cell_data) {
|
||||||
grouped_rows.push(GroupedRow {
|
grouped_rows.push(GroupedRow {
|
||||||
@ -320,7 +283,7 @@ where
|
|||||||
fn did_delete_delete_row(
|
fn did_delete_delete_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
row: &Row,
|
row: &Row,
|
||||||
field: &Field,
|
_field: &Field,
|
||||||
) -> FlowyResult<DidMoveGroupRowResult> {
|
) -> FlowyResult<DidMoveGroupRowResult> {
|
||||||
// if the cell_rev is none, then the row must in the default group.
|
// if the cell_rev is none, then the row must in the default group.
|
||||||
let mut result = DidMoveGroupRowResult {
|
let mut result = DidMoveGroupRowResult {
|
||||||
@ -328,9 +291,8 @@ where
|
|||||||
row_changesets: vec![],
|
row_changesets: vec![],
|
||||||
};
|
};
|
||||||
if let Some(cell) = row.cells.get(&self.grouping_field_id) {
|
if let Some(cell) = row.cells.get(&self.grouping_field_id) {
|
||||||
let cell_bytes = get_cell_protobuf(cell, field, None);
|
let cell_data = <T as TypeOption>::CellData::from(cell);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
if !cell_data.is_cell_empty() {
|
||||||
if !cell_data.is_empty() {
|
|
||||||
tracing::error!("did_delete_delete_row {:?}", cell);
|
tracing::error!("did_delete_delete_row {:?}", cell);
|
||||||
result.row_changesets = self.delete_row(row, &cell_data);
|
result.row_changesets = self.delete_row(row, &cell_data);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
@ -380,13 +342,24 @@ where
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_group_setting_changeset(&mut self, changeset: GroupChangesets) -> FlowyResult<()> {
|
async fn apply_group_changeset(
|
||||||
for group_changeset in changeset.update_groups {
|
&mut self,
|
||||||
if let Err(e) = self.context.update_group(group_changeset) {
|
changeset: &GroupChangesets,
|
||||||
tracing::error!("Failed to update group: {:?}", e);
|
) -> FlowyResult<TypeOptionData> {
|
||||||
|
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(
|
fn apply_group_configuration_setting_changeset(
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
use std::sync::Arc;
|
use async_trait::async_trait;
|
||||||
|
use collab_database::fields::{Field, TypeOptionData};
|
||||||
use collab_database::fields::Field;
|
|
||||||
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB};
|
use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB};
|
||||||
use crate::services::cell::insert_checkbox_cell;
|
use crate::services::cell::insert_checkbox_cell;
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOption, CHECK, UNCHECK,
|
CheckboxCellDataParser, CheckboxTypeOption, TypeOption, CHECK, UNCHECK,
|
||||||
};
|
};
|
||||||
use crate::services::group::action::GroupCustomize;
|
use crate::services::group::action::GroupCustomize;
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{BaseGroupController, GroupController};
|
||||||
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
|
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)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct CheckboxGroupConfiguration {
|
pub struct CheckboxGroupConfiguration {
|
||||||
@ -24,14 +24,15 @@ pub struct CheckboxGroupConfiguration {
|
|||||||
pub type CheckboxGroupController = BaseGroupController<
|
pub type CheckboxGroupController = BaseGroupController<
|
||||||
CheckboxGroupConfiguration,
|
CheckboxGroupConfiguration,
|
||||||
CheckboxTypeOption,
|
CheckboxTypeOption,
|
||||||
CheckboxGroupGenerator,
|
CheckboxGroupBuilder,
|
||||||
CheckboxCellDataParser,
|
CheckboxCellDataParser,
|
||||||
|
CheckboxGroupOperationInterceptorImpl,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfiguration>;
|
pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfiguration>;
|
||||||
|
|
||||||
impl GroupCustomize for CheckboxGroupController {
|
impl GroupCustomize for CheckboxGroupController {
|
||||||
type CellData = CheckboxCellData;
|
type GroupTypeOption = CheckboxTypeOption;
|
||||||
fn placeholder_cell(&self) -> Option<Cell> {
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
Some(
|
Some(
|
||||||
new_cell_builder(FieldType::Checkbox)
|
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: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> bool {
|
||||||
if cell_data.is_check() {
|
if cell_data.is_check() {
|
||||||
content == CHECK
|
content == CHECK
|
||||||
} else {
|
} else {
|
||||||
@ -51,7 +56,7 @@ impl GroupCustomize for CheckboxGroupController {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
@ -100,7 +105,11 @@ impl GroupCustomize for CheckboxGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_groups(|group| {
|
self.context.iter_mut_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
@ -118,7 +127,7 @@ impl GroupCustomize for CheckboxGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -132,7 +141,7 @@ impl GroupCustomize for CheckboxGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for CheckboxGroupController {
|
impl GroupController for CheckboxGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {
|
fn did_update_field_type_option(&mut self, _field: &Field) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,15 +163,16 @@ impl GroupController for CheckboxGroupController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CheckboxGroupGenerator();
|
pub struct CheckboxGroupBuilder();
|
||||||
impl GroupsBuilder for CheckboxGroupGenerator {
|
#[async_trait]
|
||||||
|
impl GroupsBuilder for CheckboxGroupBuilder {
|
||||||
type Context = CheckboxGroupContext;
|
type Context = CheckboxGroupContext;
|
||||||
type TypeOptionType = CheckboxTypeOption;
|
type GroupTypeOption = CheckboxTypeOption;
|
||||||
|
|
||||||
fn build(
|
async fn build(
|
||||||
_field: &Field,
|
_field: &Field,
|
||||||
_context: &Self::Context,
|
_context: &Self::Context,
|
||||||
_type_option: &Option<Self::TypeOptionType>,
|
_type_option: &Self::GroupTypeOption,
|
||||||
) -> GeneratedGroups {
|
) -> GeneratedGroups {
|
||||||
let check_group = GeneratedGroupConfig {
|
let check_group = GeneratedGroupConfig {
|
||||||
group: Group::new(CHECK.to_string(), "".to_string()),
|
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<TypeOptionData> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use std::format;
|
use std::format;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use chrono::{
|
use chrono::{
|
||||||
DateTime, Datelike, Days, Duration, Local, NaiveDate, NaiveDateTime, Offset, TimeZone,
|
DateTime, Datelike, Days, Duration, Local, NaiveDate, NaiveDateTime, Offset, TimeZone,
|
||||||
};
|
};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use collab_database::database::timestamp;
|
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 collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
@ -15,18 +15,16 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
|||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
DateCellDataPB, FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB,
|
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
|
||||||
RowMetaPB,
|
|
||||||
};
|
};
|
||||||
use crate::services::cell::insert_date_cell;
|
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::action::GroupCustomize;
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{BaseGroupController, GroupController};
|
||||||
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
|
|
||||||
};
|
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
|
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
|
||||||
|
GroupChangeset, GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait GroupConfigurationContentSerde: Sized + Send + Sync {
|
pub trait GroupConfigurationContentSerde: Sized + Send + Sync {
|
||||||
@ -63,14 +61,15 @@ pub enum DateCondition {
|
|||||||
pub type DateGroupController = BaseGroupController<
|
pub type DateGroupController = BaseGroupController<
|
||||||
DateGroupConfiguration,
|
DateGroupConfiguration,
|
||||||
DateTypeOption,
|
DateTypeOption,
|
||||||
DateGroupGenerator,
|
DateGroupBuilder,
|
||||||
DateCellDataParser,
|
DateCellDataParser,
|
||||||
|
DateGroupOperationInterceptorImpl,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub type DateGroupContext = GroupContext<DateGroupConfiguration>;
|
pub type DateGroupContext = GroupContext<DateGroupConfiguration>;
|
||||||
|
|
||||||
impl GroupCustomize for DateGroupController {
|
impl GroupCustomize for DateGroupController {
|
||||||
type CellData = DateCellDataPB;
|
type GroupTypeOption = DateTypeOption;
|
||||||
|
|
||||||
fn placeholder_cell(&self) -> Option<Cell> {
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
Some(
|
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: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> bool {
|
||||||
content
|
content
|
||||||
== group_id(
|
== group_id(
|
||||||
&cell_data.into(),
|
cell_data,
|
||||||
self.type_option.as_ref(),
|
&self.type_option,
|
||||||
&self.context.get_setting_content(),
|
&self.context.get_setting_content(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_or_delete_group_when_cell_changed(
|
fn create_or_delete_group_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
_row_detail: &RowDetail,
|
||||||
old_cell_data: Option<&Self::CellData>,
|
_old_cell_data: Option<&<Self::GroupTypeOption as TypeOption>::CellProtobufType>,
|
||||||
cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||||
let setting_content = self.context.get_setting_content();
|
let setting_content = self.context.get_setting_content();
|
||||||
let mut inserted_group = None;
|
let mut inserted_group = None;
|
||||||
if self
|
if self
|
||||||
.context
|
.context
|
||||||
.get_group(&group_id(
|
.get_group(&group_id(
|
||||||
&cell_data.into(),
|
&_cell_data.into(),
|
||||||
self.type_option.as_ref(),
|
&self.type_option,
|
||||||
&setting_content,
|
&setting_content,
|
||||||
))
|
))
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
let group = make_group_from_date_cell(
|
let group =
|
||||||
&cell_data.into(),
|
make_group_from_date_cell(&_cell_data.into(), &self.type_option, &setting_content);
|
||||||
self.type_option.as_ref(),
|
|
||||||
&setting_content,
|
|
||||||
);
|
|
||||||
let mut new_group = self.context.add_new_group(group)?;
|
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);
|
inserted_group = Some(new_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the old group if there are no rows in that 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(
|
self.context.get_group(&group_id(
|
||||||
&old_cell_data.into(),
|
&old_cell_data.into(),
|
||||||
self.type_option.as_ref(),
|
&self.type_option,
|
||||||
&setting_content,
|
&setting_content,
|
||||||
))
|
))
|
||||||
}) {
|
}) {
|
||||||
@ -148,19 +148,13 @@ impl GroupCustomize for DateGroupController {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
let setting_content = self.context.get_setting_content();
|
let setting_content = self.context.get_setting_content();
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
if group.id
|
if group.id == group_id(&cell_data.into(), &self.type_option, &setting_content) {
|
||||||
== group_id(
|
|
||||||
&cell_data.into(),
|
|
||||||
self.type_option.as_ref(),
|
|
||||||
&setting_content,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if !group.contains_row(&row_detail.row.id) {
|
if !group.contains_row(&row_detail.row.id) {
|
||||||
changeset
|
changeset
|
||||||
.inserted_rows
|
.inserted_rows
|
||||||
@ -181,7 +175,11 @@ impl GroupCustomize for DateGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_groups(|group| {
|
self.context.iter_mut_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
@ -199,7 +197,7 @@ impl GroupCustomize for DateGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -214,13 +212,13 @@ impl GroupCustomize for DateGroupController {
|
|||||||
fn delete_group_when_move_row(
|
fn delete_group_when_move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_row: &Row,
|
_row: &Row,
|
||||||
cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Option<GroupPB> {
|
) -> Option<GroupPB> {
|
||||||
let mut deleted_group = None;
|
let mut deleted_group = None;
|
||||||
let setting_content = self.context.get_setting_content();
|
let setting_content = self.context.get_setting_content();
|
||||||
if let Some((_, group)) = self.context.get_group(&group_id(
|
if let Some((_, group)) = self.context.get_group(&group_id(
|
||||||
&cell_data.into(),
|
&_cell_data.into(),
|
||||||
self.type_option.as_ref(),
|
&self.type_option,
|
||||||
&setting_content,
|
&setting_content,
|
||||||
)) {
|
)) {
|
||||||
if group.rows.len() == 1 {
|
if group.rows.len() == 1 {
|
||||||
@ -237,7 +235,7 @@ impl GroupCustomize for DateGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for DateGroupController {
|
impl GroupController for DateGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
|
fn did_update_field_type_option(&mut self, _field: &Field) {}
|
||||||
|
|
||||||
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
||||||
match self.context.get_group(group_id) {
|
match self.context.get_group(group_id) {
|
||||||
@ -257,18 +255,19 @@ impl GroupController for DateGroupController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DateGroupGenerator();
|
pub struct DateGroupBuilder();
|
||||||
impl GroupsBuilder for DateGroupGenerator {
|
#[async_trait]
|
||||||
|
impl GroupsBuilder for DateGroupBuilder {
|
||||||
type Context = DateGroupContext;
|
type Context = DateGroupContext;
|
||||||
type TypeOptionType = DateTypeOption;
|
type GroupTypeOption = DateTypeOption;
|
||||||
|
|
||||||
fn build(
|
async fn build(
|
||||||
field: &Field,
|
field: &Field,
|
||||||
context: &Self::Context,
|
context: &Self::Context,
|
||||||
type_option: &Option<Self::TypeOptionType>,
|
type_option: &Self::GroupTypeOption,
|
||||||
) -> GeneratedGroups {
|
) -> GeneratedGroups {
|
||||||
// Read all the cells for the grouping field
|
// 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
|
// Generate the groups
|
||||||
let mut group_configs: Vec<GeneratedGroupConfig> = cells
|
let mut group_configs: Vec<GeneratedGroupConfig> = cells
|
||||||
@ -276,8 +275,7 @@ impl GroupsBuilder for DateGroupGenerator {
|
|||||||
.flat_map(|value| value.into_date_field_cell_data())
|
.flat_map(|value| value.into_date_field_cell_data())
|
||||||
.filter(|cell| cell.timestamp.is_some())
|
.filter(|cell| cell.timestamp.is_some())
|
||||||
.map(|cell| {
|
.map(|cell| {
|
||||||
let group =
|
let group = make_group_from_date_cell(&cell, type_option, &context.get_setting_content());
|
||||||
make_group_from_date_cell(&cell, type_option.as_ref(), &context.get_setting_content());
|
|
||||||
GeneratedGroupConfig {
|
GeneratedGroupConfig {
|
||||||
filter_content: group.id.clone(),
|
filter_content: group.id.clone(),
|
||||||
group,
|
group,
|
||||||
@ -296,7 +294,7 @@ impl GroupsBuilder for DateGroupGenerator {
|
|||||||
|
|
||||||
fn make_group_from_date_cell(
|
fn make_group_from_date_cell(
|
||||||
cell_data: &DateCellData,
|
cell_data: &DateCellData,
|
||||||
type_option: Option<&DateTypeOption>,
|
type_option: &DateTypeOption,
|
||||||
setting_content: &str,
|
setting_content: &str,
|
||||||
) -> Group {
|
) -> Group {
|
||||||
let group_id = group_id(cell_data, type_option, setting_content);
|
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(
|
fn group_id(
|
||||||
cell_data: &DateCellData,
|
cell_data: &DateCellData,
|
||||||
type_option: Option<&DateTypeOption>,
|
type_option: &DateTypeOption,
|
||||||
setting_content: &str,
|
setting_content: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
let binding = DateTypeOption::default();
|
|
||||||
let type_option = type_option.unwrap_or(&binding);
|
|
||||||
let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default();
|
let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default();
|
||||||
let date_time = date_time_from_timestamp(cell_data.timestamp, &type_option.timezone_id);
|
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(
|
fn group_name_from_id(
|
||||||
group_id: &str,
|
group_id: &str,
|
||||||
type_option: Option<&DateTypeOption>,
|
type_option: &DateTypeOption,
|
||||||
setting_content: &str,
|
setting_content: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
let binding = DateTypeOption::default();
|
|
||||||
let type_option = type_option.unwrap_or(&binding);
|
|
||||||
let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default();
|
let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default();
|
||||||
let date = NaiveDate::parse_from_str(group_id, GROUP_ID_DATE_FORMAT).unwrap();
|
let date = NaiveDate::parse_from_str(group_id, GROUP_ID_DATE_FORMAT).unwrap();
|
||||||
|
|
||||||
@ -449,6 +443,21 @@ fn date_time_from_timestamp(timestamp: Option<i64>, 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<TypeOptionData> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::vec;
|
use std::vec;
|
||||||
@ -582,16 +591,11 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (i, test) in tests.iter().enumerate() {
|
for (i, test) in tests.iter().enumerate() {
|
||||||
let group_id = group_id(
|
let group_id = group_id(&test.cell_data, test.type_option, &test.setting_content);
|
||||||
&test.cell_data,
|
|
||||||
Some(test.type_option),
|
|
||||||
&test.setting_content,
|
|
||||||
);
|
|
||||||
assert_eq!(test.exp_group_id, group_id, "test {}", i);
|
assert_eq!(test.exp_group_id, group_id, "test {}", i);
|
||||||
|
|
||||||
if !test.exp_group_name.is_empty() {
|
if !test.exp_group_name.is_empty() {
|
||||||
let group_name =
|
let group_name = group_name_from_id(&group_id, test.type_option, &test.setting_content);
|
||||||
group_name_from_id(&group_id, Some(test.type_option), &test.setting_content);
|
|
||||||
assert_eq!(test.exp_group_name, group_name, "test {}", i);
|
assert_eq!(test.exp_group_name, group_name, "test {}", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
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 collab_database::rows::{Cells, Row, RowDetail};
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
@ -40,6 +41,7 @@ impl DefaultGroupController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl GroupControllerOperation for DefaultGroupController {
|
impl GroupControllerOperation for DefaultGroupController {
|
||||||
fn field_id(&self) -> &str {
|
fn field_id(&self) -> &str {
|
||||||
&self.field_id
|
&self.field_id
|
||||||
@ -102,8 +104,11 @@ impl GroupControllerOperation for DefaultGroupController {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_group_setting_changeset(&mut self, _changeset: GroupChangesets) -> FlowyResult<()> {
|
async fn apply_group_changeset(
|
||||||
Ok(())
|
&mut self,
|
||||||
|
_changeset: &GroupChangesets,
|
||||||
|
) -> FlowyResult<TypeOptionData> {
|
||||||
|
Ok(TypeOptionData::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_group_configuration_setting_changeset(
|
fn apply_group_configuration_setting_changeset(
|
||||||
@ -115,7 +120,7 @@ impl GroupControllerOperation for DefaultGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for DefaultGroupController {
|
impl GroupController for DefaultGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {
|
fn did_update_field_type_option(&mut self, _field: &Field) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
use std::sync::Arc;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use collab_database::fields::{Field, TypeOptionData};
|
use collab_database::fields::{Field, TypeOptionData};
|
||||||
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
||||||
use serde::{Deserialize, Serialize};
|
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::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
MultiSelectTypeOption, SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction,
|
MultiSelectTypeOption, SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction,
|
||||||
|
TypeOption,
|
||||||
};
|
};
|
||||||
use crate::services::group::action::GroupCustomize;
|
use crate::services::group::action::GroupCustomize;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{BaseGroupController, GroupController};
|
||||||
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
|
|
||||||
};
|
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
add_or_remove_select_option_row, generate_select_option_groups, make_no_status_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)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
@ -28,18 +27,20 @@ pub type MultiSelectOptionGroupContext = GroupContext<MultiSelectGroupConfigurat
|
|||||||
pub type MultiSelectGroupController = BaseGroupController<
|
pub type MultiSelectGroupController = BaseGroupController<
|
||||||
MultiSelectGroupConfiguration,
|
MultiSelectGroupConfiguration,
|
||||||
MultiSelectTypeOption,
|
MultiSelectTypeOption,
|
||||||
MultiSelectGroupGenerator,
|
MultiSelectGroupBuilder,
|
||||||
SelectOptionCellDataParser,
|
SelectOptionCellDataParser,
|
||||||
|
MultiSelectGroupOperationInterceptorImpl,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl GroupCustomize for MultiSelectGroupController {
|
impl GroupCustomize for MultiSelectGroupController {
|
||||||
type CellData = SelectOptionCellDataPB;
|
type GroupTypeOption = MultiSelectTypeOption;
|
||||||
|
|
||||||
fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool {
|
fn can_group(
|
||||||
cell_data
|
&self,
|
||||||
.select_options
|
content: &str,
|
||||||
.iter()
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
.any(|option| option.id == content)
|
) -> bool {
|
||||||
|
cell_data.iter().any(|option_id| option_id == content)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn placeholder_cell(&self) -> Option<Cell> {
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
@ -53,7 +54,7 @@ impl GroupCustomize for MultiSelectGroupController {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
@ -64,7 +65,11 @@ impl GroupCustomize for MultiSelectGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
|
||||||
@ -76,7 +81,7 @@ impl GroupCustomize for MultiSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -90,7 +95,7 @@ impl GroupCustomize for MultiSelectGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for MultiSelectGroupController {
|
impl GroupController for MultiSelectGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
|
fn did_update_field_type_option(&mut self, _field: &Field) {}
|
||||||
|
|
||||||
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
||||||
match self.context.get_group(group_id) {
|
match self.context.get_group(group_id) {
|
||||||
@ -107,49 +112,56 @@ impl GroupController for MultiSelectGroupController {
|
|||||||
group.add_row(row_detail.clone())
|
group.add_row(row_detail.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_group_name(&mut self, group_id: &str, group_name: &str) -> Option<TypeOptionData> {
|
|
||||||
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;
|
pub struct MultiSelectGroupBuilder;
|
||||||
impl GroupsBuilder for MultiSelectGroupGenerator {
|
#[async_trait]
|
||||||
|
impl GroupsBuilder for MultiSelectGroupBuilder {
|
||||||
type Context = MultiSelectOptionGroupContext;
|
type Context = MultiSelectOptionGroupContext;
|
||||||
type TypeOptionType = MultiSelectTypeOption;
|
type GroupTypeOption = MultiSelectTypeOption;
|
||||||
|
|
||||||
fn build(
|
async fn build(
|
||||||
field: &Field,
|
field: &Field,
|
||||||
_context: &Self::Context,
|
_context: &Self::Context,
|
||||||
type_option: &Option<Self::TypeOptionType>,
|
type_option: &Self::GroupTypeOption,
|
||||||
) -> GeneratedGroups {
|
) -> GeneratedGroups {
|
||||||
let group_configs = match type_option {
|
let group_configs = generate_select_option_groups(&field.id, &type_option.options);
|
||||||
None => vec![],
|
|
||||||
Some(type_option) => generate_select_option_groups(&field.id, &type_option.options),
|
|
||||||
};
|
|
||||||
|
|
||||||
GeneratedGroups {
|
GeneratedGroups {
|
||||||
no_status_group: Some(make_no_status_group(field)),
|
no_status_group: Some(make_no_status_group(field)),
|
||||||
group_configs,
|
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<TypeOptionData> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
use std::sync::Arc;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use collab_database::fields::{Field, TypeOptionData};
|
use collab_database::fields::{Field, TypeOptionData};
|
||||||
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
||||||
use serde::{Deserialize, Serialize};
|
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::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption,
|
SelectOption, SelectOptionCellDataParser, SelectTypeOptionSharedAction, SingleSelectTypeOption,
|
||||||
|
TypeOption,
|
||||||
};
|
};
|
||||||
use crate::services::group::action::GroupCustomize;
|
use crate::services::group::action::GroupCustomize;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{BaseGroupController, GroupController};
|
||||||
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
|
|
||||||
};
|
|
||||||
use crate::services::group::controller_impls::select_option_controller::util::*;
|
use crate::services::group::controller_impls::select_option_controller::util::*;
|
||||||
use crate::services::group::entities::GroupData;
|
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)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct SingleSelectGroupConfiguration {
|
pub struct SingleSelectGroupConfiguration {
|
||||||
@ -28,17 +29,19 @@ pub type SingleSelectOptionGroupContext = GroupContext<SingleSelectGroupConfigur
|
|||||||
pub type SingleSelectGroupController = BaseGroupController<
|
pub type SingleSelectGroupController = BaseGroupController<
|
||||||
SingleSelectGroupConfiguration,
|
SingleSelectGroupConfiguration,
|
||||||
SingleSelectTypeOption,
|
SingleSelectTypeOption,
|
||||||
SingleSelectGroupGenerator,
|
SingleSelectGroupBuilder,
|
||||||
SelectOptionCellDataParser,
|
SelectOptionCellDataParser,
|
||||||
|
SingleSelectGroupOperationInterceptorImpl,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl GroupCustomize for SingleSelectGroupController {
|
impl GroupCustomize for SingleSelectGroupController {
|
||||||
type CellData = SelectOptionCellDataPB;
|
type GroupTypeOption = SingleSelectTypeOption;
|
||||||
fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool {
|
fn can_group(
|
||||||
cell_data
|
&self,
|
||||||
.select_options
|
content: &str,
|
||||||
.iter()
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
.any(|option| option.id == content)
|
) -> bool {
|
||||||
|
cell_data.iter().any(|option_id| option_id == content)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn placeholder_cell(&self) -> Option<Cell> {
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
@ -52,7 +55,7 @@ impl GroupCustomize for SingleSelectGroupController {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
@ -63,7 +66,11 @@ impl GroupCustomize for SingleSelectGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
|
||||||
@ -75,7 +82,7 @@ impl GroupCustomize for SingleSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -89,7 +96,7 @@ impl GroupCustomize for SingleSelectGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for SingleSelectGroupController {
|
impl GroupController for SingleSelectGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
|
fn did_update_field_type_option(&mut self, _field: &Field) {}
|
||||||
|
|
||||||
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
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);
|
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())
|
group.add_row(row_detail.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_group_name(&mut self, group_id: &str, group_name: &str) -> Option<TypeOptionData> {
|
|
||||||
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();
|
pub struct SingleSelectGroupBuilder();
|
||||||
impl GroupsBuilder for SingleSelectGroupGenerator {
|
#[async_trait]
|
||||||
|
impl GroupsBuilder for SingleSelectGroupBuilder {
|
||||||
type Context = SingleSelectOptionGroupContext;
|
type Context = SingleSelectOptionGroupContext;
|
||||||
type TypeOptionType = SingleSelectTypeOption;
|
type GroupTypeOption = SingleSelectTypeOption;
|
||||||
fn build(
|
async fn build(
|
||||||
field: &Field,
|
field: &Field,
|
||||||
_context: &Self::Context,
|
_context: &Self::Context,
|
||||||
type_option: &Option<Self::TypeOptionType>,
|
type_option: &Self::GroupTypeOption,
|
||||||
) -> GeneratedGroups {
|
) -> GeneratedGroups {
|
||||||
let group_configs = match type_option {
|
let group_configs = generate_select_option_groups(&field.id, &type_option.options);
|
||||||
None => vec![],
|
|
||||||
Some(type_option) => generate_select_option_groups(&field.id, &type_option.options),
|
|
||||||
};
|
|
||||||
|
|
||||||
GeneratedGroups {
|
GeneratedGroups {
|
||||||
no_status_group: Some(make_no_status_group(field)),
|
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<TypeOptionData> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,9 +8,8 @@ use crate::entities::{
|
|||||||
use crate::services::cell::{
|
use crate::services::cell::{
|
||||||
insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
|
insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell,
|
||||||
};
|
};
|
||||||
use crate::services::field::{SelectOption, CHECK};
|
use crate::services::field::{SelectOption, SelectOptionIds, CHECK};
|
||||||
use crate::services::group::controller::MoveGroupRowContext;
|
use crate::services::group::{GeneratedGroupConfig, Group, GroupData, MoveGroupRowContext};
|
||||||
use crate::services::group::{GeneratedGroupConfig, Group, GroupData};
|
|
||||||
|
|
||||||
pub fn add_or_remove_select_option_row(
|
pub fn add_or_remove_select_option_row(
|
||||||
group: &mut GroupData,
|
group: &mut GroupData,
|
||||||
@ -52,12 +51,12 @@ pub fn add_or_remove_select_option_row(
|
|||||||
|
|
||||||
pub fn remove_select_option_row(
|
pub fn remove_select_option_row(
|
||||||
group: &mut GroupData,
|
group: &mut GroupData,
|
||||||
cell_data: &SelectOptionCellDataPB,
|
cell_data: &SelectOptionIds,
|
||||||
row: &Row,
|
row: &Row,
|
||||||
) -> Option<GroupRowsNotificationPB> {
|
) -> Option<GroupRowsNotificationPB> {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
cell_data.select_options.iter().for_each(|option| {
|
cell_data.iter().for_each(|option_id| {
|
||||||
if option.id == group.id && group.contains_row(&row.id) {
|
if option_id == &group.id && group.contains_row(&row.id) {
|
||||||
group.remove_row(&row.id);
|
group.remove_row(&row.id);
|
||||||
changeset.deleted_rows.push(row.id.clone().into_inner());
|
changeset.deleted_rows.push(row.id.clone().into_inner());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
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 collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -8,17 +9,16 @@ use flowy_error::FlowyResult;
|
|||||||
|
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
|
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
|
||||||
URLCellDataPB,
|
|
||||||
};
|
};
|
||||||
use crate::services::cell::insert_url_cell;
|
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::action::GroupCustomize;
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{BaseGroupController, GroupController};
|
||||||
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
|
|
||||||
};
|
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
|
make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
|
||||||
|
GroupChangeset, GroupOperationInterceptor, GroupTypeOptionCellOperation, GroupsBuilder,
|
||||||
|
MoveGroupRowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
@ -26,13 +26,18 @@ pub struct URLGroupConfiguration {
|
|||||||
pub hide_empty: bool,
|
pub hide_empty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type URLGroupController =
|
pub type URLGroupController = BaseGroupController<
|
||||||
BaseGroupController<URLGroupConfiguration, URLTypeOption, URLGroupGenerator, URLCellDataParser>;
|
URLGroupConfiguration,
|
||||||
|
URLTypeOption,
|
||||||
|
URLGroupGenerator,
|
||||||
|
URLCellDataParser,
|
||||||
|
URLGroupOperationInterceptorImpl,
|
||||||
|
>;
|
||||||
|
|
||||||
pub type URLGroupContext = GroupContext<URLGroupConfiguration>;
|
pub type URLGroupContext = GroupContext<URLGroupConfiguration>;
|
||||||
|
|
||||||
impl GroupCustomize for URLGroupController {
|
impl GroupCustomize for URLGroupController {
|
||||||
type CellData = URLCellDataPB;
|
type GroupTypeOption = URLTypeOption;
|
||||||
|
|
||||||
fn placeholder_cell(&self) -> Option<Cell> {
|
fn placeholder_cell(&self) -> Option<Cell> {
|
||||||
Some(
|
Some(
|
||||||
@ -42,15 +47,19 @@ impl GroupCustomize for URLGroupController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool {
|
fn can_group(
|
||||||
cell_data.content == content
|
&self,
|
||||||
|
content: &str,
|
||||||
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> bool {
|
||||||
|
cell_data.data == content
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_or_delete_group_when_cell_changed(
|
fn create_or_delete_group_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
_row_detail: &RowDetail,
|
||||||
_old_cell_data: Option<&Self::CellData>,
|
_old_cell_data: Option<&<Self::GroupTypeOption as TypeOption>::CellProtobufType>,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||||
// Just return if the group with this url already exists
|
// Just return if the group with this url already exists
|
||||||
let mut inserted_group = None;
|
let mut inserted_group = None;
|
||||||
@ -58,7 +67,7 @@ impl GroupCustomize for URLGroupController {
|
|||||||
let cell_data: URLCellData = _cell_data.clone().into();
|
let cell_data: URLCellData = _cell_data.clone().into();
|
||||||
let group = make_group_from_url_cell(&cell_data);
|
let group = make_group_from_url_cell(&cell_data);
|
||||||
let mut new_group = self.context.add_new_group(group)?;
|
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);
|
inserted_group = Some(new_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +99,7 @@ impl GroupCustomize for URLGroupController {
|
|||||||
fn add_or_remove_row_when_cell_changed(
|
fn add_or_remove_row_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_detail: &RowDetail,
|
row_detail: &RowDetail,
|
||||||
cell_data: &Self::CellData,
|
cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_status_groups(|group| {
|
self.context.iter_mut_status_groups(|group| {
|
||||||
@ -116,7 +125,11 @@ impl GroupCustomize for URLGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(
|
||||||
|
&mut self,
|
||||||
|
row: &Row,
|
||||||
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellData,
|
||||||
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.context.iter_mut_groups(|group| {
|
self.context.iter_mut_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
@ -134,7 +147,7 @@ impl GroupCustomize for URLGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -145,11 +158,10 @@ impl GroupCustomize for URLGroupController {
|
|||||||
});
|
});
|
||||||
group_changeset
|
group_changeset
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_group_when_move_row(
|
fn delete_group_when_move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_row: &Row,
|
_row: &Row,
|
||||||
_cell_data: &Self::CellData,
|
_cell_data: &<Self::GroupTypeOption as TypeOption>::CellProtobufType,
|
||||||
) -> Option<GroupPB> {
|
) -> Option<GroupPB> {
|
||||||
let mut deleted_group = None;
|
let mut deleted_group = None;
|
||||||
if let Some((_, group)) = self.context.get_group(&_cell_data.content) {
|
if let Some((_, group)) = self.context.get_group(&_cell_data.content) {
|
||||||
@ -167,7 +179,7 @@ impl GroupCustomize for URLGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GroupController for URLGroupController {
|
impl GroupController for URLGroupController {
|
||||||
fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
|
fn did_update_field_type_option(&mut self, _field: &Field) {}
|
||||||
|
|
||||||
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
|
||||||
match self.context.get_group(group_id) {
|
match self.context.get_group(group_id) {
|
||||||
@ -187,17 +199,18 @@ impl GroupController for URLGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct URLGroupGenerator();
|
pub struct URLGroupGenerator();
|
||||||
|
#[async_trait]
|
||||||
impl GroupsBuilder for URLGroupGenerator {
|
impl GroupsBuilder for URLGroupGenerator {
|
||||||
type Context = URLGroupContext;
|
type Context = URLGroupContext;
|
||||||
type TypeOptionType = URLTypeOption;
|
type GroupTypeOption = URLTypeOption;
|
||||||
|
|
||||||
fn build(
|
async fn build(
|
||||||
field: &Field,
|
field: &Field,
|
||||||
context: &Self::Context,
|
context: &Self::Context,
|
||||||
_type_option: &Option<Self::TypeOptionType>,
|
_type_option: &Self::GroupTypeOption,
|
||||||
) -> GeneratedGroups {
|
) -> GeneratedGroups {
|
||||||
// Read all the cells for the grouping field
|
// 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
|
// Generate the groups
|
||||||
let group_configs = cells
|
let group_configs = cells
|
||||||
@ -223,3 +236,21 @@ fn make_group_from_url_cell(cell: &URLCellData) -> Group {
|
|||||||
let group_name = cell.data.clone();
|
let group_name = cell.data.clone();
|
||||||
Group::new(group_id, group_name)
|
Group::new(group_id, group_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct URLGroupOperationInterceptorImpl {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) cell_writer: Arc<dyn GroupTypeOptionCellOperation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<TypeOptionData> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,7 +20,13 @@ pub struct GroupSettingChangeset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct GroupChangesets {
|
pub struct GroupChangesets {
|
||||||
pub update_groups: Vec<GroupChangeset>,
|
pub changesets: Vec<GroupChangeset>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<GroupChangeset>> for GroupChangesets {
|
||||||
|
fn from(changesets: Vec<GroupChangeset>) -> Self {
|
||||||
|
Self { changesets }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
|
@ -1,19 +1,82 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::RowDetail;
|
use collab_database::rows::{Cell, RowDetail, RowId};
|
||||||
use collab_database::views::DatabaseLayout;
|
use collab_database::views::DatabaseLayout;
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
|
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
|
use crate::services::field::TypeOption;
|
||||||
use crate::services::group::{
|
use crate::services::group::{
|
||||||
CheckboxGroupContext, CheckboxGroupController, DateGroupContext, DateGroupController,
|
CheckboxGroupContext, CheckboxGroupController, CheckboxGroupOperationInterceptorImpl,
|
||||||
DefaultGroupController, Group, GroupController, GroupSetting, GroupSettingReader,
|
DateGroupContext, DateGroupController, DateGroupOperationInterceptorImpl, DefaultGroupController,
|
||||||
GroupSettingWriter, MultiSelectGroupController, MultiSelectOptionGroupContext,
|
Group, GroupController, GroupSetting, GroupSettingReader, GroupSettingWriter,
|
||||||
SingleSelectGroupController, SingleSelectOptionGroupContext, URLGroupContext, URLGroupController,
|
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<Group>,
|
||||||
|
pub group_configs: Vec<GeneratedGroupConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<RowId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RowChangeset {
|
||||||
|
pub row_id: RowId,
|
||||||
|
pub height: Option<i32>,
|
||||||
|
pub visibility: Option<bool>,
|
||||||
|
// 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<String, Cell>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
/// Returns a group controller.
|
||||||
///
|
///
|
||||||
/// Each view can be grouped by one field, each field has its own 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)
|
fields(grouping_field_id=%grouping_field.id, grouping_field_type)
|
||||||
err
|
err
|
||||||
)]
|
)]
|
||||||
pub async fn make_group_controller<R, W>(
|
pub async fn make_group_controller<R, W, TW>(
|
||||||
view_id: String,
|
view_id: String,
|
||||||
grouping_field: Arc<Field>,
|
grouping_field: Arc<Field>,
|
||||||
row_details: Vec<Arc<RowDetail>>,
|
row_details: Vec<Arc<RowDetail>>,
|
||||||
setting_reader: R,
|
setting_reader: R,
|
||||||
setting_writer: W,
|
setting_writer: W,
|
||||||
|
type_option_cell_writer: TW,
|
||||||
) -> FlowyResult<Box<dyn GroupController>>
|
) -> FlowyResult<Box<dyn GroupController>>
|
||||||
where
|
where
|
||||||
R: GroupSettingReader,
|
R: GroupSettingReader,
|
||||||
W: GroupSettingWriter,
|
W: GroupSettingWriter,
|
||||||
|
TW: GroupTypeOptionCellOperation,
|
||||||
{
|
{
|
||||||
let grouping_field_type = FieldType::from(grouping_field.field_type);
|
let grouping_field_type = FieldType::from(grouping_field.field_type);
|
||||||
tracing::Span::current().record("grouping_field", &grouping_field_type.default_name());
|
tracing::Span::current().record("grouping_field", &grouping_field_type.default_name());
|
||||||
@ -48,6 +113,7 @@ where
|
|||||||
let mut group_controller: Box<dyn GroupController>;
|
let mut group_controller: Box<dyn GroupController>;
|
||||||
let configuration_reader = Arc::new(setting_reader);
|
let configuration_reader = Arc::new(setting_reader);
|
||||||
let configuration_writer = Arc::new(setting_writer);
|
let configuration_writer = Arc::new(setting_writer);
|
||||||
|
let type_option_cell_writer = Arc::new(type_option_cell_writer);
|
||||||
|
|
||||||
match grouping_field_type {
|
match grouping_field_type {
|
||||||
FieldType::SingleSelect => {
|
FieldType::SingleSelect => {
|
||||||
@ -58,7 +124,10 @@ where
|
|||||||
configuration_writer,
|
configuration_writer,
|
||||||
)
|
)
|
||||||
.await?;
|
.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);
|
group_controller = Box::new(controller);
|
||||||
},
|
},
|
||||||
FieldType::MultiSelect => {
|
FieldType::MultiSelect => {
|
||||||
@ -69,7 +138,10 @@ where
|
|||||||
configuration_writer,
|
configuration_writer,
|
||||||
)
|
)
|
||||||
.await?;
|
.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);
|
group_controller = Box::new(controller);
|
||||||
},
|
},
|
||||||
FieldType::Checkbox => {
|
FieldType::Checkbox => {
|
||||||
@ -80,7 +152,9 @@ where
|
|||||||
configuration_writer,
|
configuration_writer,
|
||||||
)
|
)
|
||||||
.await?;
|
.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);
|
group_controller = Box::new(controller);
|
||||||
},
|
},
|
||||||
FieldType::URL => {
|
FieldType::URL => {
|
||||||
@ -91,7 +165,11 @@ where
|
|||||||
configuration_writer,
|
configuration_writer,
|
||||||
)
|
)
|
||||||
.await?;
|
.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);
|
group_controller = Box::new(controller);
|
||||||
},
|
},
|
||||||
FieldType::DateTime => {
|
FieldType::DateTime => {
|
||||||
@ -102,7 +180,9 @@ where
|
|||||||
configuration_writer,
|
configuration_writer,
|
||||||
)
|
)
|
||||||
.await?;
|
.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);
|
group_controller = Box::new(controller);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -26,7 +26,7 @@ pub trait SortDelegate: Send + Sync {
|
|||||||
fn get_sort(&self, view_id: &str, sort_id: &str) -> Fut<Option<Arc<Sort>>>;
|
fn get_sort(&self, view_id: &str, sort_id: &str) -> Fut<Option<Arc<Sort>>>;
|
||||||
/// Returns all the rows after applying grid's filter
|
/// Returns all the rows after applying grid's filter
|
||||||
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
|
||||||
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
|
fn get_field(&self, field_id: &str) -> Option<Field>;
|
||||||
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ use flowy_database2::services::field::{
|
|||||||
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction,
|
edit_single_select_type_option, SelectOption, SelectTypeOptionSharedAction,
|
||||||
SingleSelectTypeOption,
|
SingleSelectTypeOption,
|
||||||
};
|
};
|
||||||
use flowy_database2::services::group::GroupSettingChangeset;
|
|
||||||
use lib_infra::util::timestamp;
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
use crate::database::database_editor::DatabaseEditorTest;
|
use crate::database::database_editor::DatabaseEditorTest;
|
||||||
@ -68,12 +67,6 @@ pub enum GroupScript {
|
|||||||
group_id: String,
|
group_id: String,
|
||||||
group_name: String,
|
group_name: String,
|
||||||
},
|
},
|
||||||
AssertGroupConfiguration {
|
|
||||||
hide_ungrouped: bool,
|
|
||||||
},
|
|
||||||
UpdateGroupConfiguration {
|
|
||||||
hide_ungrouped: Option<bool>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DatabaseGroupTest {
|
pub struct DatabaseGroupTest {
|
||||||
@ -276,25 +269,6 @@ impl DatabaseGroupTest {
|
|||||||
assert_eq!(group_id, group.group_id, "group index: {}", group_index);
|
assert_eq!(group_id, group.group_id, "group index: {}", group_index);
|
||||||
assert_eq!(group_name, group.group_name, "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();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ bytes = "1.4"
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
validator = "0.16.0"
|
validator = "0.16.0"
|
||||||
|
tokio = { version = "1.0", features = ["sync"]}
|
||||||
|
|
||||||
fancy-regex = { version = "0.11.0" }
|
fancy-regex = { version = "0.11.0" }
|
||||||
lib-dispatch = { workspace = true, optional = true }
|
lib-dispatch = { workspace = true, optional = true }
|
||||||
|
@ -157,3 +157,9 @@ impl From<fancy_regex::Error> for FlowyError {
|
|||||||
FlowyError::internal().with_context(e)
|
FlowyError::internal().with_context(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<tokio::sync::oneshot::error::RecvError> for FlowyError {
|
||||||
|
fn from(e: tokio::sync::oneshot::error::RecvError) -> Self {
|
||||||
|
FlowyError::internal().with_context(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user