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