Merge pull request #854 from AppFlowy-IO/feat/view_lens

This commit is contained in:
Nathan.fooo 2022-08-15 23:38:17 +08:00 committed by GitHub
commit 6b312b4f8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 925 additions and 385 deletions

View File

@ -935,6 +935,7 @@ dependencies = [
"atomic_refcell",
"bytes",
"chrono",
"crossbeam-utils",
"dart-notify",
"dashmap",
"diesel",

View File

@ -1,2 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE user_table;
ALTER TABLE user_table DROP COLUMN workspace;

View File

@ -1 +1,2 @@
-- This file should undo anything in `up.sql`
-- This file should undo anything in `up.sql`
ALTER TABLE view_table DROP COLUMN ext_data;

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE grid_view_rev_table;

View File

@ -0,0 +1,11 @@
-- Your SQL goes here
CREATE TABLE grid_view_rev_table (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
object_id TEXT NOT NULL DEFAULT '',
base_rev_id BIGINT NOT NULL DEFAULT 0,
rev_id BIGINT NOT NULL DEFAULT 0,
data BLOB NOT NULL DEFAULT (x''),
state INTEGER NOT NULL DEFAULT 0
);

View File

@ -177,20 +177,20 @@ macro_rules! impl_rev_state_map {
}
}
impl std::convert::From<$target> for RevisionState {
impl std::convert::From<$target> for crate::disk::RevisionState {
fn from(s: $target) -> Self {
match s {
$target::Sync => RevisionState::Sync,
$target::Ack => RevisionState::Ack,
$target::Sync => crate::disk::RevisionState::Sync,
$target::Ack => crate::disk::RevisionState::Ack,
}
}
}
impl std::convert::From<RevisionState> for $target {
fn from(s: RevisionState) -> Self {
impl std::convert::From<crate::disk::RevisionState> for $target {
fn from(s: crate::disk::RevisionState) -> Self {
match s {
RevisionState::Sync => $target::Sync,
RevisionState::Ack => $target::Ack,
crate::disk::RevisionState::Sync => $target::Sync,
crate::disk::RevisionState::Ack => $target::Ack,
}
}
}

View File

@ -42,6 +42,17 @@ table! {
}
}
table! {
grid_view_rev_table (id) {
id -> Integer,
object_id -> Text,
base_rev_id -> BigInt,
rev_id -> BigInt,
data -> Binary,
state -> Integer,
}
}
table! {
kv_table (key) {
key -> Text,
@ -125,6 +136,7 @@ allow_tables_to_appear_in_same_query!(
grid_block_index_table,
grid_meta_rev_table,
grid_rev_table,
grid_view_rev_table,
kv_table,
rev_snapshot,
rev_table,

View File

@ -4,13 +4,12 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_revision::{
RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
};
use flowy_sync::util::make_delta_from_revisions;
use flowy_sync::util::make_text_delta_from_revisions;
use flowy_sync::{
client_folder::{FolderChangeset, FolderPad},
entities::{revision::Revision, ws_data::ServerRevisionWSData},
};
use lib_infra::future::FutureResult;
use lib_ot::core::PhantomAttributes;
use parking_lot::RwLock;
use std::sync::Arc;
@ -132,7 +131,7 @@ impl FolderEditor {
pub struct FolderRevisionCompactor();
impl RevisionCompactor for FolderRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
let delta = make_text_delta_from_revisions(revisions)?;
Ok(delta.json_bytes())
}
}

View File

@ -40,6 +40,7 @@ regex = "1.5.6"
url = { version = "2"}
futures = "0.3.15"
atomic_refcell = "0.1.8"
crossbeam-utils = "0.8.7"
[dev-dependencies]
flowy-test = { path = "../flowy-test" }

View File

@ -1,4 +1,4 @@
use crate::entities::RowPB;
use crate::entities::{CreateRowParams, RowPB};
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr;
@ -11,20 +11,17 @@ pub struct CreateBoardCardPayloadPB {
#[pb(index = 2)]
pub group_id: String,
}
pub struct CreateBoardCardParams {
pub grid_id: String,
pub group_id: String,
}
impl TryInto<CreateBoardCardParams> for CreateBoardCardPayloadPB {
impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<CreateBoardCardParams, Self::Error> {
fn try_into(self) -> Result<CreateRowParams, Self::Error> {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?;
Ok(CreateBoardCardParams {
Ok(CreateRowParams {
grid_id: grid_id.0,
group_id: group_id.0,
start_row_id: None,
group_id: Some(group_id.0),
})
}
}

View File

@ -58,6 +58,7 @@ pub struct CreateRowPayloadPB {
pub struct CreateRowParams {
pub grid_id: String,
pub start_row_id: Option<String>,
pub group_id: Option<String>,
}
impl TryInto<CreateRowParams> for CreateRowPayloadPB {
@ -68,6 +69,7 @@ impl TryInto<CreateRowParams> for CreateRowPayloadPB {
Ok(CreateRowParams {
grid_id: grid_id.0,
start_row_id: self.start_row_id,
group_id: None,
})
}
}

View File

@ -266,7 +266,7 @@ pub(crate) async fn create_row_handler(
) -> DataResult<RowPB, FlowyError> {
let params: CreateRowParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
let row = editor.create_row(params.start_row_id).await?;
let row = editor.create_row(params).await?;
data_result(row)
}
@ -419,8 +419,8 @@ pub(crate) async fn create_board_card_handler(
data: Data<CreateBoardCardPayloadPB>,
manager: AppData<Arc<GridManager>>,
) -> DataResult<RowPB, FlowyError> {
let params: CreateBoardCardParams = data.into_inner().try_into()?;
let params: CreateRowParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
let row = editor.create_board_card(&params.group_id).await?;
let row = editor.create_row(params).await?;
data_result(row)
}

View File

@ -1,5 +1,6 @@
use crate::services::block_revision_editor::GridBlockRevisionCompactor;
use crate::services::block_editor::GridBlockRevisionCompactor;
use crate::services::grid_editor::{GridRevisionCompactor, GridRevisionEditor};
use crate::services::grid_view_manager::make_grid_view_rev_manager;
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::persistence::kv::GridKVPersistence;
use crate::services::persistence::migration::GridMigration;
@ -9,10 +10,10 @@ use bytes::Bytes;
use dashmap::DashMap;
use flowy_database::ConnectionPool;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{BuildGridContext, GridRevision};
use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridViewRevision};
use flowy_revision::disk::{SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionPersistence};
use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence};
use flowy_sync::client_grid::{make_grid_block_delta, make_grid_delta};
use flowy_sync::client_grid::{make_grid_block_delta, make_grid_delta, make_grid_view_delta};
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
use std::sync::Arc;
use tokio::sync::RwLock;
@ -70,6 +71,15 @@ impl GridManager {
let db_pool = self.grid_user.db_pool()?;
let rev_manager = self.make_grid_rev_manager(grid_id, db_pool)?;
let _ = rev_manager.reset_object(revisions).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
async fn create_grid_view<T: AsRef<str>>(&self, view_id: T, revisions: RepeatedRevision) -> FlowyResult<()> {
let view_id = view_id.as_ref();
let rev_manager = make_grid_view_rev_manager(&self.grid_user, view_id).await?;
let _ = rev_manager.reset_object(revisions).await?;
Ok(())
}
@ -198,14 +208,23 @@ pub async fn make_grid_view_data(
let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?;
}
let grid_rev = GridRevision::from_build_context(view_id, build_context);
let grid_id = view_id.to_owned();
let grid_rev = GridRevision::from_build_context(&grid_id, build_context);
// Create grid
let grid_meta_delta = make_grid_delta(&grid_rev);
let grid_delta_data = grid_meta_delta.json_bytes();
let grid_rev_delta = make_grid_delta(&grid_rev);
let grid_rev_delta_bytes = grid_rev_delta.json_bytes();
let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
Revision::initial_revision(user_id, &grid_id, grid_rev_delta_bytes.clone()).into();
let _ = grid_manager.create_grid(&grid_id, repeated_revision).await?;
Ok(grid_delta_data)
// Create grid view
let grid_view = GridViewRevision::new(view_id.to_owned(), view_id.to_owned());
let grid_view_delta = make_grid_view_delta(&grid_view);
let grid_view_delta_bytes = grid_view_delta.json_bytes();
let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, view_id, grid_view_delta_bytes).into();
let _ = grid_manager.create_grid_view(view_id, repeated_revision).await?;
Ok(grid_rev_delta_bytes)
}

View File

@ -5,9 +5,9 @@ use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaCh
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad};
use flowy_sync::entities::revision::Revision;
use flowy_sync::util::make_delta_from_revisions;
use flowy_sync::util::make_text_delta_from_revisions;
use lib_infra::future::FutureResult;
use lib_ot::core::PhantomAttributes;
use std::borrow::Cow;
use std::sync::Arc;
use tokio::sync::RwLock;
@ -200,7 +200,7 @@ impl RevisionObjectBuilder for GridBlockRevisionPadBuilder {
pub struct GridBlockRevisionCompactor();
impl RevisionCompactor for GridBlockRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
let delta = make_text_delta_from_revisions(revisions)?;
Ok(delta.json_bytes())
}
}

View File

@ -1,7 +1,7 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB};
use crate::manager::GridUser;
use crate::services::block_revision_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor};
use crate::services::block_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor};
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlockSnapshot};
use dashmap::DashMap;
@ -17,8 +17,6 @@ use std::sync::Arc;
type BlockId = String;
pub(crate) struct GridBlockManager {
#[allow(dead_code)]
grid_id: String,
user: Arc<dyn GridUser>,
persistence: Arc<BlockIndexCache>,
block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
@ -26,16 +24,13 @@ pub(crate) struct GridBlockManager {
impl GridBlockManager {
pub(crate) async fn new(
grid_id: &str,
user: &Arc<dyn GridUser>,
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
persistence: Arc<BlockIndexCache>,
) -> FlowyResult<Self> {
let block_editors = make_block_editors(user, block_meta_revs).await?;
let user = user.clone();
let grid_id = grid_id.to_owned();
let manager = Self {
grid_id,
user,
block_editors,
persistence,
@ -44,7 +39,7 @@ impl GridBlockManager {
}
// #[tracing::instrument(level = "trace", skip(self))]
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
pub(crate) async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
debug_assert!(!block_id.is_empty());
match self.block_editors.get(block_id) {
None => {
@ -59,13 +54,13 @@ impl GridBlockManager {
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
let block_id = self.persistence.get_block_id(row_id)?;
Ok(self.get_editor(&block_id).await?)
Ok(self.get_block_editor(&block_id).await?)
}
pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<i32> {
let block_id = row_rev.block_id.clone();
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
let editor = self.get_editor(&row_rev.block_id).await?;
let editor = self.get_block_editor(&row_rev.block_id).await?;
let mut index_row_order = InsertedRowPB::from(&row_rev);
let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
@ -82,7 +77,7 @@ impl GridBlockManager {
let mut changesets = vec![];
for (block_id, row_revs) in rows_by_block_id {
let mut inserted_row_orders = vec![];
let editor = self.get_editor(&block_id).await?;
let editor = self.get_block_editor(&block_id).await?;
let mut row_count = 0;
for row in row_revs {
let _ = self.persistence.insert(&row.block_id, &row.id)?;
@ -130,7 +125,7 @@ impl GridBlockManager {
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let row_id = row_id.to_owned();
let block_id = self.persistence.get_block_id(&row_id)?;
let editor = self.get_editor(&block_id).await?;
let editor = self.get_block_editor(&block_id).await?;
match editor.get_row_info(&row_id).await? {
None => {}
Some(row_info) => {
@ -147,7 +142,7 @@ impl GridBlockManager {
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
let mut changesets = vec![];
for grid_block in block_from_row_orders(row_orders) {
let editor = self.get_editor(&grid_block.id).await?;
let editor = self.get_block_editor(&grid_block.id).await?;
let row_ids = grid_block
.rows
.into_iter()
@ -207,7 +202,7 @@ impl GridBlockManager {
}
pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult<Vec<RowPB>> {
let editor = self.get_editor(block_id).await?;
let editor = self.get_block_editor(block_id).await?;
editor.get_row_infos::<&str>(None).await
}
@ -227,7 +222,7 @@ impl GridBlockManager {
}
Some(block_ids) => {
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let editor = self.get_block_editor(&block_id).await?;
let row_revs = editor.get_row_revs::<&str>(None).await?;
snapshots.push(GridBlockSnapshot { block_id, row_revs });
}
@ -250,6 +245,7 @@ impl GridBlockManager {
}
}
/// Initialize each block editor
async fn make_block_editors(
user: &Arc<dyn GridUser>,
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
@ -264,7 +260,7 @@ async fn make_block_editors(
}
async fn make_block_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> {
tracing::trace!("Open block:{} meta editor", block_id);
tracing::trace!("Open block:{} editor", block_id);
let token = user.token()?;
let user_id = user.user_id()?;
let pool = user.db_pool()?;

View File

@ -3,7 +3,7 @@ use crate::entities::{
SelectOptionFilterConfigurationPB, TextFilterConfigurationPB,
};
use dashmap::DashMap;
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use flowy_grid_data_model::revision::{FieldRevision, FilterConfigurationRevision, RowRevision};
use flowy_sync::client_grid::GridRevisionPad;
use std::collections::HashMap;
use std::sync::Arc;
@ -74,6 +74,7 @@ impl FilterCache {
this
}
#[allow(dead_code)]
pub(crate) fn remove(&self, filter_id: &FilterId) {
let _ = match filter_id.field_type {
FieldType::RichText => {
@ -104,13 +105,15 @@ impl FilterCache {
/// Refresh the filter according to the field id.
pub(crate) async fn refresh_filter_cache(
cache: Arc<FilterCache>,
field_ids: Option<Vec<String>>,
_field_ids: Option<Vec<String>>,
grid_pad: &Arc<RwLock<GridRevisionPad>>,
) {
let grid_pad = grid_pad.read().await;
let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default();
// let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default();
// TODO nathan
let filter_revs: Vec<Arc<FilterConfigurationRevision>> = vec![];
for filter_rev in filters_revs {
for filter_rev in filter_revs {
match grid_pad.get_field_rev(&filter_rev.field_id) {
None => {}
Some((_, field_rev)) => {

View File

@ -1,3 +1,8 @@
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::entities::{FieldType, GridBlockChangesetPB};
use crate::services::block_manager::GridBlockManager;
@ -22,8 +27,10 @@ use std::sync::Arc;
use tokio::sync::RwLock;
pub(crate) struct GridFilterService {
#[allow(dead_code)]
scheduler: Arc<dyn GridServiceTaskScheduler>,
grid_pad: Arc<RwLock<GridRevisionPad>>,
#[allow(dead_code)]
block_manager: Arc<GridBlockManager>,
filter_cache: Arc<FilterCache>,
filter_result_cache: Arc<FilterResultCache>,

View File

@ -5,14 +5,13 @@ use crate::manager::{GridTaskSchedulerRwLock, GridUser};
use crate::services::block_manager::GridBlockManager;
use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes};
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
use crate::services::filter::{GridFilterChangeset, GridFilterService};
use crate::services::group::GridGroupService;
use crate::services::filter::GridFilterService;
use crate::services::grid_view_manager::GridViewManager;
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::{
make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder,
};
use crate::services::setting::make_grid_setting;
use bytes::Bytes;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::revision::*;
@ -21,26 +20,23 @@ use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeseri
use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
use flowy_sync::entities::revision::Revision;
use flowy_sync::errors::CollaborateResult;
use flowy_sync::util::make_delta_from_revisions;
use flowy_sync::util::make_text_delta_from_revisions;
use lib_infra::future::FutureResult;
use lib_ot::core::PhantomAttributes;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct GridRevisionEditor {
pub(crate) grid_id: String,
pub grid_id: String,
user: Arc<dyn GridUser>,
grid_pad: Arc<RwLock<GridRevisionPad>>,
// view_editor: Arc<GridViewRevisionEditor>,
view_manager: Arc<GridViewManager>,
rev_manager: Arc<RevisionManager>,
block_manager: Arc<GridBlockManager>,
#[allow(dead_code)]
pub(crate) filter_service: Arc<GridFilterService>,
#[allow(dead_code)]
pub(crate) group_service: Arc<RwLock<GridGroupService>>,
}
impl Drop for GridRevisionEditor {
@ -63,20 +59,30 @@ impl GridRevisionEditor {
let rev_manager = Arc::new(rev_manager);
let grid_pad = Arc::new(RwLock::new(grid_pad));
// Block manager
let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence).await?);
let filter_service =
GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
let group_service =
GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
// View manager
let view_manager = Arc::new(
GridViewManager::new(
user.clone(),
grid_pad.clone(),
block_manager.clone(),
Arc::new(task_scheduler.clone()),
)
.await?,
);
let editor = Arc::new(Self {
grid_id: grid_id.to_owned(),
user,
grid_pad,
rev_manager,
block_manager,
view_manager,
filter_service: Arc::new(filter_service),
group_service: Arc::new(RwLock::new(group_service)),
});
Ok(editor)
@ -278,9 +284,15 @@ impl GridRevisionEditor {
Ok(())
}
pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowPB> {
let row_rev = self.create_row_rev().await?;
self.create_row_pb(row_rev, start_row_id).await
pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult<RowPB> {
let mut row_rev = self.create_row_rev().await?;
self.view_manager.update_row(&mut row_rev, &params).await;
let row_pb = self.create_row_pb(row_rev, params.start_row_id.clone()).await?;
self.view_manager.did_create_row(&row_pb, &params).await;
Ok(row_pb)
}
pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
@ -330,7 +342,7 @@ impl GridRevisionEditor {
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let _ = self.block_manager.delete_row(row_id).await?;
self.group_service.read().await.did_delete_card(row_id.to_owned()).await;
self.view_manager.delete_row(row_id).await;
Ok(())
}
@ -444,34 +456,15 @@ impl GridRevisionEditor {
}
pub async fn get_grid_setting(&self) -> FlowyResult<GridSettingPB> {
let read_guard = self.grid_pad.read().await;
let grid_setting_rev = read_guard.get_setting_rev();
let field_revs = read_guard.get_field_revs(None)?;
let grid_setting = make_grid_setting(grid_setting_rev, &field_revs);
Ok(grid_setting)
self.view_manager.get_setting().await
}
pub async fn get_grid_filter(&self) -> FlowyResult<Vec<GridFilterConfiguration>> {
let read_guard = self.grid_pad.read().await;
match read_guard.get_filters(None) {
Some(filter_revs) => Ok(filter_revs
.iter()
.map(|filter_rev| filter_rev.as_ref().into())
.collect::<Vec<GridFilterConfiguration>>()),
None => Ok(vec![]),
}
self.view_manager.get_filters().await
}
pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
let filter_changeset = GridFilterChangeset::from(&params);
let _ = self
.modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
.await?;
let filter_service = self.filter_service.clone();
tokio::spawn(async move {
filter_service.apply_changeset(filter_changeset).await;
});
let _ = self.view_manager.update_setting(params).await?;
Ok(())
}
@ -521,22 +514,9 @@ impl GridRevisionEditor {
}
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
match self.block_manager.get_row_rev(row_id).await? {
None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
Some(row_rev) => {
let _ = self
.block_manager
.move_row(row_rev.clone(), from as usize, to as usize)
.await?;
}
}
Ok(())
self.view_manager.move_row(row_id, from, to).await
}
pub async fn move_board_card(&self, group_id: &str, from: i32, to: i32) -> FlowyResult<()> {
self.group_service.write().await.move_card(group_id, from, to).await;
Ok(())
}
pub async fn delta_bytes(&self) -> Bytes {
self.grid_pad.read().await.delta_bytes()
}
@ -549,7 +529,10 @@ impl GridRevisionEditor {
let mut blocks_meta_data = vec![];
if original_blocks.len() == duplicated_blocks.len() {
for (index, original_block_meta) in original_blocks.iter().enumerate() {
let grid_block_meta_editor = self.block_manager.get_editor(&original_block_meta.block_id).await?;
let grid_block_meta_editor = self
.block_manager
.get_block_editor(&original_block_meta.block_id)
.await?;
let duplicated_block_id = &duplicated_blocks[index].block_id;
tracing::trace!("Duplicate block:{} meta data", duplicated_block_id);
@ -568,24 +551,9 @@ impl GridRevisionEditor {
})
}
pub async fn create_board_card(&self, group_id: &str) -> FlowyResult<RowPB> {
let mut row_rev = self.create_row_rev().await?;
let _ = self
.group_service
.write()
.await
.update_board_card(&mut row_rev, group_id)
.await;
let row_pb = self.create_row_pb(row_rev, None).await?;
self.group_service.read().await.did_create_card(group_id, &row_pb).await;
Ok(row_pb)
}
#[tracing::instrument(level = "trace", skip_all, err)]
pub async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
let groups = self.group_service.write().await.load_groups().await.unwrap_or_default();
Ok(RepeatedGridGroupPB { items: groups })
self.view_manager.load_groups().await
}
async fn create_row_rev(&self) -> FlowyResult<RowRevision> {
@ -716,7 +684,7 @@ impl RevisionCloudService for GridRevisionCloudService {
pub struct GridRevisionCompactor();
impl RevisionCompactor for GridRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
let delta = make_text_delta_from_revisions(revisions)?;
Ok(delta.json_bytes())
}
}

View File

@ -1,31 +1,163 @@
use flowy_error::{FlowyError, FlowyResult};
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, RowPB};
use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService};
use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision};
use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder};
use flowy_sync::client_grid::GridViewRevisionPad;
use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
use flowy_sync::entities::revision::Revision;
use lib_infra::future::FutureResult;
use crate::services::setting::make_grid_setting;
use flowy_sync::entities::grid::GridSettingChangesetParams;
use lib_infra::future::{wrap_future, AFFuture, FutureResult};
use std::sync::Arc;
use tokio::sync::RwLock;
pub trait GridViewRevisionDelegate: Send + Sync + 'static {
fn get_field_revs(&self) -> AFFuture<Vec<Arc<FieldRevision>>>;
fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>>;
}
pub trait GridViewRevisionDataSource: Send + Sync + 'static {
fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>>;
}
#[allow(dead_code)]
pub struct GridViewRevisionEditor {
#[allow(dead_code)]
user_id: String,
view_id: String,
pad: Arc<RwLock<GridViewRevisionPad>>,
#[allow(dead_code)]
rev_manager: Arc<RevisionManager>,
delegate: Arc<dyn GridViewRevisionDelegate>,
data_source: Arc<dyn GridViewRevisionDataSource>,
group_service: Arc<RwLock<GroupService>>,
scheduler: Arc<dyn GridServiceTaskScheduler>,
}
impl GridViewRevisionEditor {
#[allow(dead_code)]
pub async fn new(token: &str, mut rev_manager: RevisionManager) -> FlowyResult<Self> {
pub(crate) async fn new<Delegate, DataSource>(
user_id: &str,
token: &str,
view_id: String,
delegate: Delegate,
data_source: DataSource,
scheduler: Arc<dyn GridServiceTaskScheduler>,
mut rev_manager: RevisionManager,
) -> FlowyResult<Self>
where
Delegate: GridViewRevisionDelegate,
DataSource: GridViewRevisionDataSource,
{
let cloud = Arc::new(GridViewRevisionCloudService {
token: token.to_owned(),
});
let view_revision_pad = rev_manager.load::<GridViewRevisionPadBuilder>(Some(cloud)).await?;
let pad = Arc::new(RwLock::new(view_revision_pad));
let rev_manager = Arc::new(rev_manager);
let group_service = GroupService::new(Box::new(pad.clone())).await;
let user_id = user_id.to_owned();
Ok(Self {
pad,
user_id,
view_id,
rev_manager,
scheduler,
delegate: Arc::new(delegate),
data_source: Arc::new(data_source),
group_service: Arc::new(RwLock::new(group_service)),
})
}
Ok(Self { pad, rev_manager })
pub(crate) async fn create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
match params.group_id.as_ref() {
None => {}
Some(group_id) => {
self.group_service
.read()
.await
.update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id))
.await;
}
}
todo!()
}
pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
match params.group_id.as_ref() {
None => {}
Some(group_id) => {
self.group_service.read().await.did_create_row(group_id, row_pb).await;
}
}
}
pub(crate) async fn delete_row(&self, row_id: &str) {
self.group_service.read().await.did_delete_card(row_id.to_owned()).await;
}
pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
let field_revs = self.delegate.get_field_revs().await;
let row_revs = self.data_source.row_revs().await;
let mut write_guard = self.group_service.write().await;
match write_guard.load_groups(&field_revs, row_revs).await {
None => Ok(vec![]),
Some(groups) => Ok(groups),
}
}
pub(crate) async fn get_setting(&self) -> GridSettingPB {
let field_revs = self.delegate.get_field_revs().await;
let grid_setting = make_grid_setting(self.pad.read().await.get_setting_rev(), &field_revs);
grid_setting
}
pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> {
let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await;
Ok(())
}
pub(crate) async fn get_filters(&self) -> Vec<GridFilterConfiguration> {
let field_revs = self.delegate.get_field_revs().await;
match self.pad.read().await.get_setting_rev().get_all_filters(&field_revs) {
None => vec![],
Some(filters) => filters
.into_values()
.flatten()
.map(|filter| GridFilterConfiguration::from(filter.as_ref()))
.collect(),
}
}
async fn modify<F>(&self, f: F) -> FlowyResult<()>
where
F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,
{
let mut write_guard = self.pad.write().await;
match f(&mut *write_guard)? {
None => {}
Some(change) => {
let _ = self.apply_change(change).await?;
}
}
Ok(())
}
async fn apply_change(&self, change: GridViewRevisionChangeset) -> FlowyResult<()> {
let GridViewRevisionChangeset { delta, md5 } = change;
let user_id = self.user_id.clone();
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.json_bytes();
let revision = Revision::new(
&self.rev_manager.object_id,
base_rev_id,
rev_id,
delta_data,
&user_id,
md5,
);
let _ = self.rev_manager.add_local_revision(&revision).await?;
Ok(())
}
}
@ -50,3 +182,20 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
Ok(pad)
}
}
impl GroupConfigurationDelegate for Arc<RwLock<GridViewRevisionPad>> {
fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision> {
let view_pad = self.clone();
wrap_future(async move {
let grid_pad = view_pad.read().await;
let configurations = grid_pad.get_groups(&field_rev.id, &field_rev.field_type_rev);
match configurations {
None => default_group_configuration(&field_rev),
Some(mut configurations) => {
assert_eq!(configurations.len(), 1);
(&*configurations.pop().unwrap()).clone()
}
}
})
}
}

View File

@ -0,0 +1,217 @@
use crate::manager::GridUser;
use crate::services::grid_view_editor::{GridViewRevisionDataSource, GridViewRevisionDelegate, GridViewRevisionEditor};
use bytes::Bytes;
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB};
use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::block_manager::GridBlockManager;
use dashmap::DashMap;
use flowy_error::FlowyResult;
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
use flowy_sync::client_grid::GridRevisionPad;
use flowy_sync::entities::revision::Revision;
use flowy_sync::util::make_text_delta_from_revisions;
use flowy_sync::entities::grid::GridSettingChangesetParams;
use lib_infra::future::{wrap_future, AFFuture};
use std::sync::Arc;
use tokio::sync::RwLock;
type ViewId = String;
pub(crate) struct GridViewManager {
user: Arc<dyn GridUser>,
grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>,
view_editors: DashMap<ViewId, Arc<GridViewRevisionEditor>>,
scheduler: Arc<dyn GridServiceTaskScheduler>,
}
impl GridViewManager {
pub(crate) async fn new(
user: Arc<dyn GridUser>,
grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>,
scheduler: Arc<dyn GridServiceTaskScheduler>,
) -> FlowyResult<Self> {
Ok(Self {
user,
grid_pad,
scheduler,
block_manager,
view_editors: DashMap::default(),
})
}
pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
for view_editor in self.view_editors.iter() {
view_editor.create_row(row_rev, params).await;
}
}
pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
for view_editor in self.view_editors.iter() {
view_editor.did_create_row(row_pb, params).await;
}
}
pub(crate) async fn delete_row(&self, row_id: &str) {
for view_editor in self.view_editors.iter() {
view_editor.delete_row(row_id).await;
}
}
pub(crate) async fn get_setting(&self) -> FlowyResult<GridSettingPB> {
let view_editor = self.get_default_view_editor().await?;
Ok(view_editor.get_setting().await)
}
pub(crate) async fn update_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
let view_editor = self.get_default_view_editor().await?;
let _ = view_editor.update_setting(params).await?;
Ok(())
}
pub(crate) async fn get_filters(&self) -> FlowyResult<Vec<GridFilterConfiguration>> {
let view_editor = self.get_default_view_editor().await?;
Ok(view_editor.get_filters().await)
}
pub(crate) async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
let view_editor = self.get_default_view_editor().await?;
let groups = view_editor.load_groups().await?;
Ok(RepeatedGridGroupPB { items: groups })
}
pub(crate) async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
match self.block_manager.get_row_rev(row_id).await? {
None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
Some(row_rev) => {
let _ = self
.block_manager
.move_row(row_rev.clone(), from as usize, to as usize)
.await?;
}
}
Ok(())
}
pub(crate) async fn get_view_editor(&self, view_id: &str) -> FlowyResult<Arc<GridViewRevisionEditor>> {
debug_assert!(!view_id.is_empty());
match self.view_editors.get(view_id) {
None => {
let editor = Arc::new(
make_view_editor(
&self.user,
view_id,
self.grid_pad.clone(),
self.block_manager.clone(),
self.scheduler.clone(),
)
.await?,
);
self.view_editors.insert(view_id.to_owned(), editor.clone());
Ok(editor)
}
Some(view_editor) => Ok(view_editor.clone()),
}
}
async fn get_default_view_editor(&self) -> FlowyResult<Arc<GridViewRevisionEditor>> {
let grid_id = self.grid_pad.read().await.grid_id();
self.get_view_editor(&grid_id).await
}
}
async fn make_view_editor<Delegate, DataSource>(
user: &Arc<dyn GridUser>,
view_id: &str,
delegate: Delegate,
data_source: DataSource,
scheduler: Arc<dyn GridServiceTaskScheduler>,
) -> FlowyResult<GridViewRevisionEditor>
where
Delegate: GridViewRevisionDelegate,
DataSource: GridViewRevisionDataSource,
{
tracing::trace!("Open view:{} editor", view_id);
let rev_manager = make_grid_view_rev_manager(user, view_id).await?;
let user_id = user.user_id()?;
let token = user.token()?;
let view_id = view_id.to_owned();
GridViewRevisionEditor::new(&user_id, &token, view_id, delegate, data_source, scheduler, rev_manager).await
}
pub async fn make_grid_view_rev_manager(user: &Arc<dyn GridUser>, view_id: &str) -> FlowyResult<RevisionManager> {
tracing::trace!("Open view:{} editor", view_id);
let user_id = user.user_id()?;
let pool = user.db_pool()?;
let disk_cache = SQLiteGridViewRevisionPersistence::new(&user_id, pool.clone());
let rev_persistence = RevisionPersistence::new(&user_id, view_id, disk_cache);
let rev_compactor = GridViewRevisionCompactor();
let snapshot_persistence = SQLiteRevisionSnapshotPersistence::new(view_id, pool);
Ok(RevisionManager::new(
&user_id,
view_id,
rev_persistence,
rev_compactor,
snapshot_persistence,
))
}
pub struct GridViewRevisionCompactor();
impl RevisionCompactor for GridViewRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_text_delta_from_revisions(revisions)?;
Ok(delta.json_bytes())
}
}
impl GridViewRevisionDataSource for Arc<GridBlockManager> {
fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>> {
let block_manager = self.clone();
wrap_future(async move {
let blocks = block_manager.get_block_snapshots(None).await.unwrap();
blocks
.into_iter()
.map(|block| block.row_revs)
.flatten()
.collect::<Vec<Arc<RowRevision>>>()
})
}
}
impl GridViewRevisionDelegate for Arc<RwLock<GridRevisionPad>> {
fn get_field_revs(&self) -> AFFuture<Vec<Arc<FieldRevision>>> {
let pad = self.clone();
wrap_future(async move {
match pad.read().await.get_field_revs(None) {
Ok(field_revs) => field_revs,
Err(e) => {
tracing::error!("[GridViewRevisionDelegate] get field revisions failed: {}", e);
vec![]
}
}
})
}
fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>> {
let pad = self.clone();
let field_id = field_id.to_owned();
wrap_future(async move {
pad.read()
.await
.get_field_rev(&field_id)
.map(|(_, field_rev)| field_rev.clone())
})
}
}

View File

@ -4,9 +4,7 @@ use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use std::sync::Arc;
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
use crate::services::group::{
Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
};
use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable};
pub type CheckboxGroupController =
GroupController<CheckboxGroupConfigurationPB, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser>;
@ -45,7 +43,6 @@ impl GroupGenerator for CheckboxGroupGenerator {
fn generate_groups(
_configuration: &Option<Self::ConfigurationType>,
_type_option: &Option<Self::TypeOptionType>,
_cell_content_provider: &dyn GroupCellContentProvider,
) -> Vec<Group> {
let check_group = Group {
id: "true".to_string(),

View File

@ -11,14 +11,6 @@ use indexmap::IndexMap;
use std::marker::PhantomData;
use std::sync::Arc;
pub trait GroupCellContentProvider {
/// We need to group the rows base on the deduplication cell content when the field type is
/// RichText.
fn deduplication_cell_content(&self, _field_id: &str) -> Vec<String> {
vec![]
}
}
pub trait GroupGenerator {
type ConfigurationType;
type TypeOptionType;
@ -26,7 +18,6 @@ pub trait GroupGenerator {
fn generate_groups(
configuration: &Option<Self::ConfigurationType>,
type_option: &Option<Self::TypeOptionType>,
cell_content_provider: &dyn GroupCellContentProvider,
) -> Vec<Group>;
}
@ -86,18 +77,14 @@ where
T: TypeOptionDataDeserializer,
G: GroupGenerator<ConfigurationType = C, TypeOptionType = T>,
{
pub fn new(
field_rev: &Arc<FieldRevision>,
configuration: GroupConfigurationRevision,
cell_content_provider: &dyn GroupCellContentProvider,
) -> FlowyResult<Self> {
pub fn new(field_rev: &Arc<FieldRevision>, configuration: GroupConfigurationRevision) -> FlowyResult<Self> {
let configuration = match configuration.content {
None => None,
Some(content) => Some(C::try_from(Bytes::from(content))?),
};
let field_type_rev = field_rev.field_type_rev;
let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
let groups = G::generate_groups(&configuration, &type_option, cell_content_provider);
let groups = G::generate_groups(&configuration, &type_option);
let default_group = Group {
id: DEFAULT_GROUP_ID.to_owned(),

View File

@ -8,9 +8,7 @@ use std::sync::Arc;
use crate::services::field::{
MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
};
use crate::services::group::{
Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
};
use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable};
// SingleSelect
pub type SingleSelectGroupController = GroupController<
@ -59,7 +57,6 @@ impl GroupGenerator for SingleSelectGroupGenerator {
fn generate_groups(
_configuration: &Option<Self::ConfigurationType>,
type_option: &Option<Self::TypeOptionType>,
_cell_content_provider: &dyn GroupCellContentProvider,
) -> Vec<Group> {
match type_option {
None => vec![],
@ -125,7 +122,6 @@ impl GroupGenerator for MultiSelectGroupGenerator {
fn generate_groups(
_configuration: &Option<Self::ConfigurationType>,
type_option: &Option<Self::TypeOptionType>,
_cell_content_provider: &dyn GroupCellContentProvider,
) -> Vec<Group> {
match type_option {
None => vec![],

View File

@ -4,55 +4,42 @@ use crate::entities::{
NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB,
UrlGroupConfigurationPB,
};
use crate::services::block_manager::GridBlockManager;
use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::group::{
CheckboxGroupController, GroupActionHandler, GroupCellContentProvider, MultiSelectGroupController,
SingleSelectGroupController,
CheckboxGroupController, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController,
};
use bytes::Bytes;
use flowy_error::FlowyResult;
use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision};
use flowy_sync::client_grid::GridRevisionPad;
use lib_infra::future::AFFuture;
use std::future::Future;
use std::sync::Arc;
use tokio::sync::RwLock;
pub(crate) struct GridGroupService {
#[allow(dead_code)]
scheduler: Arc<dyn GridServiceTaskScheduler>,
grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>,
group_action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
pub trait GroupConfigurationDelegate: Send + Sync + 'static {
fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision>;
}
impl GridGroupService {
pub(crate) async fn new<S: GridServiceTaskScheduler>(
grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>,
scheduler: S,
) -> Self {
let scheduler = Arc::new(scheduler);
pub(crate) struct GroupService {
delegate: Box<dyn GroupConfigurationDelegate>,
action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
}
impl GroupService {
pub(crate) async fn new(delegate: Box<dyn GroupConfigurationDelegate>) -> Self {
Self {
scheduler,
grid_pad,
block_manager,
group_action_handler: None,
delegate,
action_handler: None,
}
}
pub(crate) async fn load_groups(&mut self) -> Option<Vec<GroupPB>> {
let field_rev = find_group_field(self.grid_pad.read().await.fields()).unwrap();
pub(crate) async fn load_groups(
&mut self,
field_revs: &[Arc<FieldRevision>],
row_revs: Vec<Arc<RowRevision>>,
) -> Option<Vec<GroupPB>> {
let field_rev = find_group_field(field_revs).unwrap();
let field_type: FieldType = field_rev.field_type_rev.into();
let configuration = self.get_group_configuration(&field_rev).await;
let blocks = self.block_manager.get_block_snapshots(None).await.unwrap();
let row_revs = blocks
.into_iter()
.map(|block| block.row_revs)
.flatten()
.collect::<Vec<Arc<RowRevision>>>();
let configuration = self.delegate.get_group_configuration(field_rev.clone()).await;
match self
.build_groups(&field_type, &field_rev, row_revs, configuration)
@ -63,36 +50,25 @@ impl GridGroupService {
}
}
#[tracing::instrument(level = "debug", skip(self, row_rev))]
pub(crate) async fn update_board_card(&self, row_rev: &mut RowRevision, group_id: &str) {
if let Some(group_action_handler) = self.group_action_handler.as_ref() {
pub(crate) async fn update_row<F, O>(&self, row_rev: &mut RowRevision, group_id: &str, f: F)
where
F: FnOnce(String) -> O,
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
{
if let Some(group_action_handler) = self.action_handler.as_ref() {
let field_id = group_action_handler.read().await.field_id().to_owned();
match self.grid_pad.read().await.get_field_rev(&field_id) {
None => tracing::warn!("Fail to create card because the field does not exist"),
Some((_, field_rev)) => {
match f(field_id).await {
None => {}
Some(field_rev) => {
group_action_handler
.write()
.await
.update_card(row_rev, field_rev, group_id);
.update_card(row_rev, &field_rev, group_id);
}
}
}
}
pub(crate) async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
let grid_pad = self.grid_pad.read().await;
let setting = grid_pad.get_setting_rev();
let configurations = setting.get_groups(&field_rev.id, &field_rev.field_type_rev);
match configurations {
None => default_group_configuration(field_rev),
Some(mut configurations) => {
assert_eq!(configurations.len(), 1);
(&*configurations.pop().unwrap()).clone()
}
}
}
#[allow(dead_code)]
pub async fn move_card(&self, _group_id: &str, _from: i32, _to: i32) {
// BoardCardChangesetPB {
// group_id: "".to_string(),
@ -103,20 +79,20 @@ impl GridGroupService {
// let row_pb = make_row_from_row_rev(row_rev);
todo!()
}
#[allow(dead_code)]
pub async fn did_delete_card(&self, _row_id: String) {
// let changeset = BoardCardChangesetPB::delete(group_id.to_owned(), vec![row_id]);
// self.notify_did_update_board(changeset).await;
todo!()
}
pub async fn did_create_card(&self, group_id: &str, row_pb: &RowPB) {
pub async fn did_create_row(&self, group_id: &str, row_pb: &RowPB) {
let changeset = BoardCardChangesetPB::insert(group_id.to_owned(), vec![row_pb.clone()]);
self.notify_did_update_board(changeset).await;
}
pub async fn notify_did_update_board(&self, changeset: BoardCardChangesetPB) {
if self.group_action_handler.is_none() {
if self.action_handler.is_none() {
return;
}
send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard)
@ -143,16 +119,16 @@ impl GridGroupService {
// let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
}
FieldType::SingleSelect => {
let controller = SingleSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
let controller = SingleSelectGroupController::new(field_rev, configuration)?;
self.action_handler = Some(Arc::new(RwLock::new(controller)));
}
FieldType::MultiSelect => {
let controller = MultiSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
let controller = MultiSelectGroupController::new(field_rev, configuration)?;
self.action_handler = Some(Arc::new(RwLock::new(controller)));
}
FieldType::Checkbox => {
let controller = CheckboxGroupController::new(field_rev, configuration, &self.grid_pad)?;
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
let controller = CheckboxGroupController::new(field_rev, configuration)?;
self.action_handler = Some(Arc::new(RwLock::new(controller)));
}
FieldType::URL => {
// let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
@ -160,7 +136,7 @@ impl GridGroupService {
};
let mut groups = vec![];
if let Some(group_action_handler) = self.group_action_handler.as_ref() {
if let Some(group_action_handler) = self.action_handler.as_ref() {
let mut write_guard = group_action_handler.write().await;
let _ = write_guard.group_rows(&row_revs, field_rev)?;
groups = write_guard.get_groups();
@ -182,9 +158,7 @@ fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevisi
field_rev
}
impl GroupCellContentProvider for Arc<RwLock<GridRevisionPad>> {}
fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
let field_type: FieldType = field_rev.field_type_rev.into();
let bytes: Bytes = match field_type {
FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(),

View File

@ -1,13 +1,14 @@
mod util;
pub mod block_editor;
mod block_manager;
pub mod block_revision_editor;
pub mod cell;
pub mod field;
mod filter;
pub mod grid_editor;
mod grid_editor_task;
pub mod grid_view_editor;
pub mod grid_view_manager;
pub mod group;
pub mod persistence;
pub mod row;

View File

@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{FieldType, GridCellIdParams, RowPB};
use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, RowPB};
use flowy_grid::services::field::*;
use flowy_grid_data_model::revision::{
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
@ -77,7 +77,12 @@ impl GridRowTest {
pub async fn run_script(&mut self, script: RowScript) {
match script {
RowScript::CreateEmptyRow => {
let row_order = self.editor.create_row(None).await.unwrap();
let params = CreateRowParams {
grid_id: self.editor.grid_id.clone(),
start_row_id: None,
group_id: None,
};
let row_order = self.editor.create_row(params).await.unwrap();
self.row_order_by_row_id
.insert(row_order.row_id().to_owned(), row_order);
self.row_revs = self.get_row_revs().await;

View File

@ -1,5 +1,5 @@
use crate::cache::disk::RevisionDiskCache;
use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
use crate::disk::{RevisionChangeset, RevisionRecord};
use bytes::Bytes;
use diesel::{sql_types::Integer, update, SqliteConnection};
use flowy_database::{

View File

@ -1,5 +1,5 @@
use crate::cache::disk::RevisionDiskCache;
use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
use crate::disk::{RevisionChangeset, RevisionRecord};
use bytes::Bytes;
use diesel::{sql_types::Integer, update, SqliteConnection};
use flowy_database::{
@ -103,7 +103,7 @@ impl GridMetaRevisionSql {
record.revision.object_id,
record.revision.rev_id
);
let rev_state: GridMetaRevisionState = record.state.into();
let rev_state: GridBlockRevisionState = record.state.into();
(
dsl::object_id.eq(record.revision.object_id),
dsl::base_rev_id.eq(record.revision.base_rev_id),
@ -121,7 +121,7 @@ impl GridMetaRevisionSql {
}
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: GridMetaRevisionState = changeset.state.clone().into();
let state: GridBlockRevisionState = changeset.state.clone().into();
let filter = dsl::grid_meta_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref()))
.filter(dsl::object_id.eq(changeset.object_id));
@ -146,7 +146,7 @@ impl GridMetaRevisionSql {
if let Some(rev_ids) = rev_ids {
sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
}
let rows = sql.order(dsl::rev_id.asc()).load::<GridMetaRevisionTable>(conn)?;
let rows = sql.order(dsl::rev_id.asc()).load::<GridBlockRevisionTable>(conn)?;
let records = rows
.into_iter()
.map(|row| mk_revision_record_from_table(user_id, row))
@ -166,7 +166,7 @@ impl GridMetaRevisionSql {
.filter(dsl::rev_id.le(range.end))
.filter(dsl::object_id.eq(object_id))
.order(dsl::rev_id.asc())
.load::<GridMetaRevisionTable>(conn)?;
.load::<GridBlockRevisionTable>(conn)?;
let revisions = rev_tables
.into_iter()
@ -192,31 +192,31 @@ impl GridMetaRevisionSql {
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
#[table_name = "grid_meta_rev_table"]
struct GridMetaRevisionTable {
struct GridBlockRevisionTable {
id: i32,
object_id: String,
base_rev_id: i64,
rev_id: i64,
data: Vec<u8>,
state: GridMetaRevisionState,
state: GridBlockRevisionState,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
#[repr(i32)]
#[sql_type = "Integer"]
pub enum GridMetaRevisionState {
pub enum GridBlockRevisionState {
Sync = 0,
Ack = 1,
}
impl_sql_integer_expression!(GridMetaRevisionState);
impl_rev_state_map!(GridMetaRevisionState);
impl std::default::Default for GridMetaRevisionState {
impl_sql_integer_expression!(GridBlockRevisionState);
impl_rev_state_map!(GridBlockRevisionState);
impl std::default::Default for GridBlockRevisionState {
fn default() -> Self {
GridMetaRevisionState::Sync
GridBlockRevisionState::Sync
}
}
fn mk_revision_record_from_table(user_id: &str, table: GridMetaRevisionTable) -> RevisionRecord {
fn mk_revision_record_from_table(user_id: &str, table: GridBlockRevisionTable) -> RevisionRecord {
let md5 = md5(&table.data);
let revision = Revision::new(
&table.object_id,

View File

@ -1,6 +1,5 @@
use crate::cache::disk::RevisionDiskCache;
use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
use crate::disk::{RevisionChangeset, RevisionRecord};
use bytes::Bytes;
use diesel::{sql_types::Integer, update, SqliteConnection};
use flowy_database::{

View File

@ -0,0 +1,233 @@
use crate::disk::{RevisionChangeset, RevisionDiskCache, RevisionRecord};
use bytes::Bytes;
use diesel::{sql_types::Integer, update, SqliteConnection};
use flowy_database::{
impl_sql_integer_expression, insert_or_ignore_into,
prelude::*,
schema::{grid_view_rev_table, grid_view_rev_table::dsl},
ConnectionPool,
};
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_sync::{
entities::revision::{Revision, RevisionRange},
util::md5,
};
use std::sync::Arc;
pub struct SQLiteGridViewRevisionPersistence {
user_id: String,
pub(crate) pool: Arc<ConnectionPool>,
}
impl SQLiteGridViewRevisionPersistence {
pub fn new(user_id: &str, pool: Arc<ConnectionPool>) -> Self {
Self {
user_id: user_id.to_owned(),
pool,
}
}
}
impl RevisionDiskCache for SQLiteGridViewRevisionPersistence {
type Error = FlowyError;
fn create_revision_records(&self, revision_records: Vec<RevisionRecord>) -> Result<(), Self::Error> {
let conn = self.pool.get().map_err(internal_error)?;
let _ = GridViewRevisionSql::create(revision_records, &*conn)?;
Ok(())
}
fn read_revision_records(
&self,
object_id: &str,
rev_ids: Option<Vec<i64>>,
) -> Result<Vec<RevisionRecord>, Self::Error> {
let conn = self.pool.get().map_err(internal_error)?;
let records = GridViewRevisionSql::read(&self.user_id, object_id, rev_ids, &*conn)?;
Ok(records)
}
fn read_revision_records_with_range(
&self,
object_id: &str,
range: &RevisionRange,
) -> Result<Vec<RevisionRecord>, Self::Error> {
let conn = &*self.pool.get().map_err(internal_error)?;
let revisions = GridViewRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?;
Ok(revisions)
}
fn update_revision_record(&self, changesets: Vec<RevisionChangeset>) -> FlowyResult<()> {
let conn = &*self.pool.get().map_err(internal_error)?;
let _ = conn.immediate_transaction::<_, FlowyError, _>(|| {
for changeset in changesets {
let _ = GridViewRevisionSql::update(changeset, conn)?;
}
Ok(())
})?;
Ok(())
}
fn delete_revision_records(&self, object_id: &str, rev_ids: Option<Vec<i64>>) -> Result<(), Self::Error> {
let conn = &*self.pool.get().map_err(internal_error)?;
let _ = GridViewRevisionSql::delete(object_id, rev_ids, conn)?;
Ok(())
}
fn delete_and_insert_records(
&self,
object_id: &str,
deleted_rev_ids: Option<Vec<i64>>,
inserted_records: Vec<RevisionRecord>,
) -> Result<(), Self::Error> {
let conn = self.pool.get().map_err(internal_error)?;
conn.immediate_transaction::<_, FlowyError, _>(|| {
let _ = GridViewRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?;
let _ = GridViewRevisionSql::create(inserted_records, &*conn)?;
Ok(())
})
}
}
struct GridViewRevisionSql();
impl GridViewRevisionSql {
fn create(revision_records: Vec<RevisionRecord>, conn: &SqliteConnection) -> Result<(), FlowyError> {
// Batch insert: https://diesel.rs/guides/all-about-inserts.html
let records = revision_records
.into_iter()
.map(|record| {
tracing::trace!(
"[GridViewRevisionSql] create revision: {}:{:?}",
record.revision.object_id,
record.revision.rev_id
);
let rev_state: GridViewRevisionState = record.state.into();
(
dsl::object_id.eq(record.revision.object_id),
dsl::base_rev_id.eq(record.revision.base_rev_id),
dsl::rev_id.eq(record.revision.rev_id),
dsl::data.eq(record.revision.delta_data),
dsl::state.eq(rev_state),
)
})
.collect::<Vec<_>>();
let _ = insert_or_ignore_into(dsl::grid_view_rev_table)
.values(&records)
.execute(conn)?;
Ok(())
}
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: GridViewRevisionState = changeset.state.clone().into();
let filter = dsl::grid_view_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref()))
.filter(dsl::object_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!(
"[GridViewRevisionSql] update revision:{} state:to {:?}",
changeset.rev_id,
changeset.state
);
Ok(())
}
fn read(
user_id: &str,
object_id: &str,
rev_ids: Option<Vec<i64>>,
conn: &SqliteConnection,
) -> Result<Vec<RevisionRecord>, FlowyError> {
let mut sql = dsl::grid_view_rev_table
.filter(dsl::object_id.eq(object_id))
.into_boxed();
if let Some(rev_ids) = rev_ids {
sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
}
let rows = sql.order(dsl::rev_id.asc()).load::<GridViewRevisionTable>(conn)?;
let records = rows
.into_iter()
.map(|row| mk_revision_record_from_table(user_id, row))
.collect::<Vec<_>>();
Ok(records)
}
fn read_with_range(
user_id: &str,
object_id: &str,
range: RevisionRange,
conn: &SqliteConnection,
) -> Result<Vec<RevisionRecord>, FlowyError> {
let rev_tables = dsl::grid_view_rev_table
.filter(dsl::rev_id.ge(range.start))
.filter(dsl::rev_id.le(range.end))
.filter(dsl::object_id.eq(object_id))
.order(dsl::rev_id.asc())
.load::<GridViewRevisionTable>(conn)?;
let revisions = rev_tables
.into_iter()
.map(|table| mk_revision_record_from_table(user_id, table))
.collect::<Vec<_>>();
Ok(revisions)
}
fn delete(object_id: &str, rev_ids: Option<Vec<i64>>, conn: &SqliteConnection) -> Result<(), FlowyError> {
let mut sql = diesel::delete(dsl::grid_view_rev_table).into_boxed();
sql = sql.filter(dsl::object_id.eq(object_id));
if let Some(rev_ids) = rev_ids {
tracing::trace!("[GridViewRevisionSql] Delete revision: {}:{:?}", object_id, rev_ids);
sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
}
let affected_row = sql.execute(conn)?;
tracing::trace!("[GridViewRevisionSql] Delete {} rows", affected_row);
Ok(())
}
}
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
#[table_name = "grid_view_rev_table"]
struct GridViewRevisionTable {
id: i32,
object_id: String,
base_rev_id: i64,
rev_id: i64,
data: Vec<u8>,
state: GridViewRevisionState,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
#[repr(i32)]
#[sql_type = "Integer"]
pub enum GridViewRevisionState {
Sync = 0,
Ack = 1,
}
impl_sql_integer_expression!(GridViewRevisionState);
impl_rev_state_map!(GridViewRevisionState);
impl std::default::Default for GridViewRevisionState {
fn default() -> Self {
GridViewRevisionState::Sync
}
}
fn mk_revision_record_from_table(user_id: &str, table: GridViewRevisionTable) -> RevisionRecord {
let md5 = md5(&table.data);
let revision = Revision::new(
&table.object_id,
table.base_rev_id,
table.rev_id,
Bytes::from(table.data),
user_id,
md5,
);
RevisionRecord {
revision,
state: table.state.into(),
write_to_disk: false,
}
}

View File

@ -1,10 +1,12 @@
mod document_impl;
mod grid_block_impl;
mod grid_impl;
mod grid_view_impl;
pub use document_impl::*;
pub use grid_block_impl::*;
pub use grid_impl::*;
pub use grid_view_impl::*;
use flowy_error::FlowyResult;
use flowy_sync::entities::revision::{RevId, Revision, RevisionRange};

View File

@ -1,4 +1,4 @@
use crate::revision::{GridBlockRevision, SettingRevision};
use crate::revision::GridBlockRevision;
use bytes::Bytes;
use indexmap::IndexMap;
use nanoid::nanoid;
@ -23,9 +23,6 @@ pub struct GridRevision {
pub grid_id: String,
pub fields: Vec<Arc<FieldRevision>>,
pub blocks: Vec<Arc<GridBlockMetaRevision>>,
#[serde(default)]
pub setting: SettingRevision,
}
impl GridRevision {
@ -34,7 +31,6 @@ impl GridRevision {
grid_id: grid_id.to_owned(),
fields: vec![],
blocks: vec![],
setting: SettingRevision::default(),
}
}
@ -43,7 +39,6 @@ impl GridRevision {
grid_id: grid_id.to_owned(),
fields: context.field_revs,
blocks: context.blocks.into_iter().map(Arc::new).collect(),
setting: Default::default(),
}
}
}

View File

@ -2,6 +2,7 @@ use crate::revision::SettingRevision;
use nanoid::nanoid;
use serde::{Deserialize, Serialize};
#[allow(dead_code)]
pub fn gen_grid_view_id() -> String {
nanoid!(6)
}
@ -21,9 +22,9 @@ pub struct GridViewRevision {
}
impl GridViewRevision {
pub fn new(grid_id: String) -> Self {
pub fn new(grid_id: String, view_id: String) -> Self {
GridViewRevision {
view_id: gen_grid_view_id(),
view_id,
grid_id,
setting: Default::default(),
row_orders: vec![],

View File

@ -6,8 +6,5 @@ fn grid_default_serde_test() {
let grid = GridRevision::new(&grid_id);
let json = serde_json::to_string(&grid).unwrap();
assert_eq!(
json,
r#"{"grid_id":"1","fields":[],"blocks":[],"setting":{"layout":0,"filters":[],"groups":[]}}"#
)
assert_eq!(json, r#"{"grid_id":"1","fields":[],"blocks":[]}"#)
}

View File

@ -1,5 +1,5 @@
use crate::entities::folder::FolderDelta;
use crate::util::make_delta_from_revisions;
use crate::util::make_text_delta_from_revisions;
use crate::{
client_folder::{default_folder_delta, FolderPad},
entities::revision::Revision,
@ -7,7 +7,7 @@ use crate::{
};
use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
use lib_ot::core::PhantomAttributes;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
@ -37,7 +37,7 @@ impl FolderPadBuilder {
}
pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
let mut folder_delta: FolderDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
let mut folder_delta: FolderDelta = make_text_delta_from_revisions(revisions)?;
if folder_delta.is_empty() {
folder_delta = default_folder_delta();
}

View File

@ -1,14 +1,11 @@
use crate::entities::grid::{
CreateGridFilterParams, CreateGridGroupParams, FieldChangesetParams, GridSettingChangesetParams,
};
use crate::entities::grid::FieldChangesetParams;
use crate::entities::revision::{md5, RepeatedRevision, Revision};
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
use crate::util::{cal_diff, make_delta_from_revisions};
use crate::util::{cal_diff, make_text_delta_from_revisions};
use bytes::Bytes;
use flowy_grid_data_model::revision::{
gen_block_id, gen_grid_filter_id, gen_grid_group_id, gen_grid_id, FieldRevision, FieldTypeRevision,
FilterConfigurationRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, GridRevision,
GroupConfigurationRevision, SettingRevision,
gen_block_id, gen_grid_id, FieldRevision, FieldTypeRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset,
GridRevision,
};
use lib_infra::util::move_vec_element;
use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
@ -68,7 +65,7 @@ impl GridRevisionPad {
}
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
let grid_delta: GridRevisionDelta = make_text_delta_from_revisions(revisions)?;
Self::from_delta(grid_delta)
}
@ -347,95 +344,6 @@ impl GridRevisionPad {
})
}
pub fn get_setting_rev(&self) -> &SettingRevision {
&self.grid_rev.setting
}
/// If layout is None, then the default layout will be the read from GridSettingRevision
pub fn get_filters(&self, field_ids: Option<Vec<String>>) -> Option<Vec<Arc<FilterConfigurationRevision>>> {
let mut filter_revs = vec![];
let field_revs = self.get_field_revs(None).ok()?;
field_revs.iter().for_each(|field_rev| {
let mut is_contain = true;
if let Some(field_ids) = &field_ids {
is_contain = field_ids.contains(&field_rev.id);
}
if is_contain {
// Only return the filters for the current fields' type.
let field_id = &field_rev.id;
let field_type_rev = &field_rev.field_type_rev;
if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(field_id, field_type_rev) {
filter_revs.append(&mut t_filter_revs);
}
}
});
Some(filter_revs)
}
pub fn update_grid_setting_rev(
&mut self,
changeset: GridSettingChangesetParams,
) -> CollaborateResult<Option<GridRevisionChangeset>> {
self.modify_grid(|grid_rev| {
let mut is_changed = None;
if let Some(params) = changeset.insert_filter {
grid_rev
.setting
.insert_filter(&params.field_id, &params.field_type_rev, make_filter_revision(&params));
is_changed = Some(())
}
if let Some(params) = changeset.delete_filter {
if let Some(filters) = grid_rev
.setting
.get_mut_filters(&params.field_id, &params.field_type_rev)
{
filters.retain(|filter| filter.id != params.filter_id);
}
}
if let Some(params) = changeset.insert_group {
grid_rev
.setting
.insert_group(&params.field_id, &params.field_type_rev, make_group_revision(&params));
is_changed = Some(());
}
if let Some(params) = changeset.delete_group {
if let Some(groups) = grid_rev
.setting
.get_mut_groups(&params.field_id, &params.field_type_rev)
{
groups.retain(|filter| filter.id != params.group_id);
}
}
if let Some(_sort) = changeset.insert_sort {
// let rev = GridSortRevision {
// id: gen_grid_sort_id(),
// field_id: sort.field_id,
// };
//
// grid_rev
// .setting
// .sorts
// .entry(layout_rev.clone())
// .or_insert_with(std::vec::Vec::new)
// .push(rev);
is_changed = Some(())
}
if let Some(_delete_sort_id) = changeset.delete_sort {
// match grid_rev.setting.sorts.get_mut(&layout_rev) {
// Some(sorts) => sorts.retain(|sort| sort.id != delete_sort_id),
// None => {
// tracing::warn!("Can't find the sort with {:?}", layout_rev);
// }
// }
}
Ok(is_changed)
})
}
pub fn md5(&self) -> String {
md5(&self.delta.json_bytes())
}
@ -548,21 +456,3 @@ impl std::default::Default for GridRevisionPad {
}
}
}
fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision {
FilterConfigurationRevision {
id: gen_grid_filter_id(),
field_id: params.field_id.clone(),
condition: params.condition,
content: params.content.clone(),
}
}
fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision {
GroupConfigurationRevision {
id: gen_grid_group_id(),
field_id: params.field_id.clone(),
field_type_rev: params.field_type_rev,
content: params.content.clone(),
}
}

View File

@ -1,9 +1,11 @@
use crate::entities::grid::{CreateGridFilterParams, CreateGridGroupParams, GridSettingChangesetParams};
use crate::entities::revision::{md5, Revision};
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
use crate::util::{cal_diff, make_text_delta_from_revisions};
use flowy_grid_data_model::revision::{
FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision,
GroupConfigurationRevision, GroupConfigurationsByFieldId, SortConfigurationsByFieldId,
gen_grid_filter_id, gen_grid_group_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision,
FilterConfigurationsByFieldId, GridViewRevision, GroupConfigurationRevision, GroupConfigurationsByFieldId,
SettingRevision, SortConfigurationsByFieldId,
};
use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
use std::sync::Arc;
@ -23,8 +25,8 @@ impl std::ops::Deref for GridViewRevisionPad {
}
impl GridViewRevisionPad {
pub fn new(grid_id: String) -> Self {
let view = Arc::new(GridViewRevision::new(grid_id));
pub fn new(grid_id: String, view_id: String) -> Self {
let view = Arc::new(GridViewRevision::new(grid_id, view_id));
let json = serde_json::to_string(&view).unwrap();
let delta = TextDeltaBuilder::new().insert(&json).build();
Self { view, delta }
@ -48,6 +50,59 @@ impl GridViewRevisionPad {
Self::from_delta(delta)
}
pub fn get_setting_rev(&self) -> &SettingRevision {
&self.view.setting
}
pub fn update_setting(
&mut self,
changeset: GridSettingChangesetParams,
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
self.modify(|view| {
let mut is_changed = None;
if let Some(params) = changeset.insert_filter {
view.setting.filters.insert_object(
&params.field_id,
&params.field_type_rev,
make_filter_revision(&params),
);
is_changed = Some(())
}
if let Some(params) = changeset.delete_filter {
if let Some(filters) = view
.setting
.filters
.get_mut_objects(&params.field_id, &params.field_type_rev)
{
filters.retain(|filter| filter.id != params.filter_id);
is_changed = Some(())
}
}
if let Some(params) = changeset.insert_group {
view.setting.groups.remove_all();
view.setting.groups.insert_object(
&params.field_id,
&params.field_type_rev,
make_group_revision(&params),
);
is_changed = Some(());
}
if let Some(params) = changeset.delete_group {
if let Some(groups) = view
.setting
.groups
.get_mut_objects(&params.field_id, &params.field_type_rev)
{
groups.retain(|group| group.id != params.group_id);
is_changed = Some(());
}
}
Ok(is_changed)
})
}
pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
self.setting.groups.get_all_objects(field_revs)
}
@ -161,6 +216,24 @@ impl GridViewRevisionPad {
}
}
fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision {
FilterConfigurationRevision {
id: gen_grid_filter_id(),
field_id: params.field_id.clone(),
condition: params.condition,
content: params.content.clone(),
}
}
fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision {
GroupConfigurationRevision {
id: gen_grid_group_id(),
field_id: params.field_id.clone(),
field_type_rev: params.field_type_rev,
content: params.content.clone(),
}
}
pub struct GridViewRevisionChangeset {
pub delta: TextDelta,
pub md5: String,
@ -171,3 +244,8 @@ pub fn make_grid_view_rev_json_str(grid_revision: &GridViewRevision) -> Collabor
.map_err(|err| internal_error(format!("Serialize grid view to json str failed. {:?}", err)))?;
Ok(json)
}
pub fn make_grid_view_delta(grid_view: &GridViewRevision) -> TextDelta {
let json = serde_json::to_string(grid_view).unwrap();
TextDeltaBuilder::new().insert(&json).build()
}

View File

@ -8,20 +8,20 @@ use std::{
task::{Context, Poll},
};
pub fn wrap_future<T, O>(f: T) -> FnFuture<O>
pub fn wrap_future<T, O>(f: T) -> AFFuture<O>
where
T: Future<Output = O> + Send + Sync + 'static,
{
FnFuture { fut: Box::pin(f) }
AFFuture { fut: Box::pin(f) }
}
#[pin_project]
pub struct FnFuture<T> {
pub struct AFFuture<T> {
#[pin]
pub fut: Pin<Box<dyn Future<Output = T> + Sync + Send>>,
}
impl<T> Future for FnFuture<T>
impl<T> Future for AFFuture<T>
where
T: Send + Sync,
{