Browse Source

fix: create the default group for grid

appflowy 2 năm trước cách đây
mục cha
commit
44ad0a2623

+ 6 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart

@@ -283,7 +283,12 @@ class IGridCellController<T, D> extends Equatable {
     _loadDataOperation?.cancel();
     _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
       _cellDataLoader.loadData().then((data) {
-        _cellsCache.insert(_cacheKey, GridCell(object: data));
+        if (data != null) {
+          _cellsCache.insert(_cacheKey, GridCell(object: data));
+        } else {
+          _cellsCache.remove(_cacheKey);
+        }
+
         _cellDataNotifier?.value = data;
       });
     });

+ 10 - 2
frontend/rust-lib/flowy-folder/src/manager.rs

@@ -221,8 +221,9 @@ impl DefaultFolderBuilder {
                     initial_quill_delta_string()
                 };
                 let _ = view_controller.set_latest_view(&view.id);
+                let layout_type = ViewLayoutTypePB::from(view.layout.clone());
                 let _ = view_controller
-                    .create_view(&view.id, ViewDataTypePB::Text, Bytes::from(view_data))
+                    .create_view(&view.id, ViewDataTypePB::Text, layout_type, Bytes::from(view_data))
                     .await?;
             }
         }
@@ -249,7 +250,13 @@ impl FolderManager {
 pub trait ViewDataProcessor {
     fn initialize(&self) -> FutureResult<(), FlowyError>;
 
-    fn create_container(&self, user_id: &str, view_id: &str, delta_data: Bytes) -> FutureResult<(), FlowyError>;
+    fn create_container(
+        &self,
+        user_id: &str,
+        view_id: &str,
+        layout: ViewLayoutTypePB,
+        delta_data: Bytes,
+    ) -> FutureResult<(), FlowyError>;
 
     fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
 
@@ -267,6 +274,7 @@ pub trait ViewDataProcessor {
         user_id: &str,
         view_id: &str,
         data: Vec<u8>,
+        layout: ViewLayoutTypePB,
     ) -> FutureResult<Bytes, FlowyError>;
 
     fn data_type(&self) -> ViewDataTypePB;

+ 17 - 4
frontend/rust-lib/flowy-folder/src/services/view/controller.rs

@@ -1,5 +1,5 @@
 pub use crate::entities::view::ViewDataTypePB;
-use crate::entities::ViewInfoPB;
+use crate::entities::{ViewInfoPB, ViewLayoutTypePB};
 use crate::manager::{ViewDataProcessor, ViewDataProcessorMap};
 use crate::{
     dart_notification::{send_dart_notification, FolderNotification},
@@ -67,10 +67,20 @@ impl ViewController {
             params.view_content_data = view_data.to_vec();
         } else {
             let delta_data = processor
-                .create_view_from_delta_data(&user_id, &params.view_id, params.view_content_data.clone())
+                .create_view_from_delta_data(
+                    &user_id,
+                    &params.view_id,
+                    params.view_content_data.clone(),
+                    params.layout.clone(),
+                )
                 .await?;
             let _ = self
-                .create_view(&params.view_id, params.data_type.clone(), delta_data)
+                .create_view(
+                    &params.view_id,
+                    params.data_type.clone(),
+                    params.layout.clone(),
+                    delta_data,
+                )
                 .await?;
         };
 
@@ -84,6 +94,7 @@ impl ViewController {
         &self,
         view_id: &str,
         data_type: ViewDataTypePB,
+        layout_type: ViewLayoutTypePB,
         delta_data: Bytes,
     ) -> Result<(), FlowyError> {
         if delta_data.is_empty() {
@@ -91,7 +102,9 @@ impl ViewController {
         }
         let user_id = self.user.user_id()?;
         let processor = self.get_data_processor(data_type)?;
-        let _ = processor.create_container(&user_id, view_id, delta_data).await?;
+        let _ = processor
+            .create_container(&user_id, view_id, layout_type, delta_data)
+            .await?;
         Ok(())
     }
 

+ 3 - 1
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -1,3 +1,4 @@
+use crate::entities::GridLayout;
 use crate::services::block_editor::GridBlockRevisionCompactor;
 use crate::services::grid_editor::{GridRevisionCompactor, GridRevisionEditor};
 use crate::services::grid_view_manager::make_grid_view_rev_manager;
@@ -178,6 +179,7 @@ impl GridManager {
 pub async fn make_grid_view_data(
     user_id: &str,
     view_id: &str,
+    layout: GridLayout,
     grid_manager: Arc<GridManager>,
     build_context: BuildGridContext,
 ) -> FlowyResult<Bytes> {
@@ -208,7 +210,7 @@ pub async fn make_grid_view_data(
     let _ = grid_manager.create_grid(&grid_id, repeated_revision).await?;
 
     // Create grid view
-    let grid_view = GridViewRevision::new(grid_id, view_id.to_owned());
+    let grid_view = GridViewRevision::new(grid_id, view_id.to_owned(), layout.into());
     let grid_view_delta = make_grid_view_delta(&grid_view);
     let grid_view_delta_bytes = grid_view_delta.json_bytes();
     let repeated_revision: RepeatedRevision =

+ 9 - 7
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -277,6 +277,7 @@ impl GridViewRevisionEditor {
         Ok(())
     }
 
+    #[tracing::instrument(level = "debug", skip_all, err)]
     pub(crate) async fn group_by_field(&self, field_id: &str) -> FlowyResult<()> {
         if let Some(field_rev) = self.field_delegate.get_field_rev(field_id).await {
             let new_group_controller = new_group_controller_with_field_rev(
@@ -374,13 +375,14 @@ impl GridViewRevisionEditor {
 async fn new_group_controller(
     user_id: String,
     view_id: String,
-    pad: Arc<RwLock<GridViewRevisionPad>>,
+    view_rev_pad: Arc<RwLock<GridViewRevisionPad>>,
     rev_manager: Arc<RevisionManager>,
     field_delegate: Arc<dyn GridViewFieldDelegate>,
     row_delegate: Arc<dyn GridViewRowDelegate>,
 ) -> FlowyResult<Box<dyn GroupController>> {
-    let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
+    let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
     let field_revs = field_delegate.get_field_revs().await;
+    let layout = view_rev_pad.read().await.layout();
     // Read the group field or find a new group field
     let field_rev = configuration_reader
         .get_configuration()
@@ -391,24 +393,24 @@ async fn new_group_controller(
                 .find(|field_rev| field_rev.id == configuration.field_id)
                 .cloned()
         })
-        .unwrap_or_else(|| find_group_field(&field_revs).unwrap());
+        .unwrap_or_else(|| find_group_field(&field_revs, &layout).unwrap());
 
-    new_group_controller_with_field_rev(user_id, view_id, pad, rev_manager, field_rev, row_delegate).await
+    new_group_controller_with_field_rev(user_id, view_id, view_rev_pad, rev_manager, field_rev, row_delegate).await
 }
 
 async fn new_group_controller_with_field_rev(
     user_id: String,
     view_id: String,
-    pad: Arc<RwLock<GridViewRevisionPad>>,
+    view_rev_pad: Arc<RwLock<GridViewRevisionPad>>,
     rev_manager: Arc<RevisionManager>,
     field_rev: Arc<FieldRevision>,
     row_delegate: Arc<dyn GridViewRowDelegate>,
 ) -> FlowyResult<Box<dyn GroupController>> {
-    let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
+    let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
     let configuration_writer = GroupConfigurationWriterImpl {
         user_id,
         rev_manager,
-        view_pad: pad,
+        view_pad: view_rev_pad,
     };
     let row_revs = row_delegate.gv_row_revs().await;
     make_group_controller(view_id, field_rev, row_revs, configuration_reader, configuration_writer).await

+ 12 - 10
frontend/rust-lib/flowy-grid/src/services/group/group_util.rs

@@ -8,7 +8,7 @@ use crate::services::group::{
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
     CheckboxGroupConfigurationRevision, DateGroupConfigurationRevision, FieldRevision, GroupConfigurationRevision,
-    NumberGroupConfigurationRevision, RowRevision, SelectOptionGroupConfigurationRevision,
+    LayoutRevision, NumberGroupConfigurationRevision, RowRevision, SelectOptionGroupConfigurationRevision,
     TextGroupConfigurationRevision, UrlGroupConfigurationRevision,
 };
 use std::sync::Arc;
@@ -62,15 +62,17 @@ where
     Ok(group_controller)
 }
 
-pub fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevision>> {
-    let field_rev = field_revs
-        .iter()
-        .find(|field_rev| {
-            let field_type: FieldType = field_rev.ty.into();
-            field_type.can_be_group()
-        })
-        .cloned();
-    field_rev
+pub fn find_group_field(field_revs: &[Arc<FieldRevision>], layout: &LayoutRevision) -> Option<Arc<FieldRevision>> {
+    match layout {
+        LayoutRevision::Table => field_revs.iter().find(|field_rev| field_rev.is_primary).cloned(),
+        LayoutRevision::Board => field_revs
+            .iter()
+            .find(|field_rev| {
+                let field_type: FieldType = field_rev.ty.into();
+                field_type.can_be_group()
+            })
+            .cloned(),
+    }
 }
 
 pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {

+ 38 - 7
frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs

@@ -7,6 +7,7 @@ use flowy_folder::{
     event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
     manager::FolderManager,
 };
+use flowy_grid::entities::GridLayout;
 use flowy_grid::manager::{make_grid_view_data, GridManager};
 use flowy_grid::util::{make_default_board, make_default_grid};
 use flowy_grid_data_model::revision::BuildGridContext;
@@ -142,7 +143,15 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
         FutureResult::new(async move { manager.init() })
     }
 
-    fn create_container(&self, user_id: &str, view_id: &str, delta_data: Bytes) -> FutureResult<(), FlowyError> {
+    fn create_container(
+        &self,
+        user_id: &str,
+        view_id: &str,
+        layout: ViewLayoutTypePB,
+        delta_data: Bytes,
+    ) -> FutureResult<(), FlowyError> {
+        // Only accept Document type
+        debug_assert_eq!(layout, ViewLayoutTypePB::Document);
         let repeated_revision: RepeatedRevision = Revision::initial_revision(user_id, view_id, delta_data).into();
         let view_id = view_id.to_string();
         let manager = self.0.clone();
@@ -196,7 +205,9 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
         _user_id: &str,
         _view_id: &str,
         data: Vec<u8>,
+        layout: ViewLayoutTypePB,
     ) -> FutureResult<Bytes, FlowyError> {
+        debug_assert_eq!(layout, ViewLayoutTypePB::Document);
         FutureResult::new(async move { Ok(Bytes::from(data)) })
     }
 
@@ -211,7 +222,13 @@ impl ViewDataProcessor for GridViewDataProcessor {
         FutureResult::new(async { Ok(()) })
     }
 
-    fn create_container(&self, user_id: &str, view_id: &str, delta_data: Bytes) -> FutureResult<(), FlowyError> {
+    fn create_container(
+        &self,
+        user_id: &str,
+        view_id: &str,
+        _layout: ViewLayoutTypePB,
+        delta_data: Bytes,
+    ) -> FutureResult<(), FlowyError> {
         let repeated_revision: RepeatedRevision = Revision::initial_revision(user_id, view_id, delta_data).into();
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
@@ -246,19 +263,22 @@ impl ViewDataProcessor for GridViewDataProcessor {
         view_id: &str,
         layout: ViewLayoutTypePB,
     ) -> FutureResult<Bytes, FlowyError> {
-        let build_context = match layout {
-            ViewLayoutTypePB::Grid => make_default_grid(),
-            ViewLayoutTypePB::Board => make_default_board(),
+        let (build_context, layout) = match layout {
+            ViewLayoutTypePB::Grid => (make_default_grid(), GridLayout::Table),
+            ViewLayoutTypePB::Board => (make_default_board(), GridLayout::Board),
             ViewLayoutTypePB::Document => {
                 return FutureResult::new(async move {
                     Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
                 });
             }
         };
+
         let user_id = user_id.to_string();
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
-        FutureResult::new(async move { make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await })
+        FutureResult::new(
+            async move { make_grid_view_data(&user_id, &view_id, layout, grid_manager, build_context).await },
+        )
     }
 
     fn create_view_from_delta_data(
@@ -266,15 +286,26 @@ impl ViewDataProcessor for GridViewDataProcessor {
         user_id: &str,
         view_id: &str,
         data: Vec<u8>,
+        layout: ViewLayoutTypePB,
     ) -> FutureResult<Bytes, FlowyError> {
         let user_id = user_id.to_string();
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
 
+        let layout = match layout {
+            ViewLayoutTypePB::Grid => GridLayout::Table,
+            ViewLayoutTypePB::Board => GridLayout::Board,
+            ViewLayoutTypePB::Document => {
+                return FutureResult::new(async move {
+                    Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
+                });
+            }
+        };
+
         FutureResult::new(async move {
             let bytes = Bytes::from(data);
             let build_context = BuildGridContext::try_from(bytes)?;
-            make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await
+            make_grid_view_data(&user_id, &view_id, layout, grid_manager, build_context).await
         })
     }
 

+ 2 - 2
shared-lib/flowy-grid-data-model/src/revision/grid_view.rs

@@ -48,11 +48,11 @@ pub struct GridViewRevision {
 }
 
 impl GridViewRevision {
-    pub fn new(grid_id: String, view_id: String) -> Self {
+    pub fn new(grid_id: String, view_id: String, layout: LayoutRevision) -> Self {
         GridViewRevision {
             view_id,
             grid_id,
-            layout: Default::default(),
+            layout,
             filters: Default::default(),
             groups: Default::default(),
             // row_orders: vec![],

+ 7 - 3
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -1,5 +1,5 @@
 use crate::entities::revision::{md5, RepeatedRevision, Revision};
-use crate::errors::{internal_error, CollaborateError, CollaborateResult};
+use crate::errors::{internal_error, CollaborateError, CollaborateResult };
 use crate::util::{cal_diff, make_text_delta_from_revisions};
 use bytes::Bytes;
 use flowy_grid_data_model::revision::{
@@ -103,8 +103,12 @@ impl GridRevisionPad {
             |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
                 None => Ok(None),
                 Some(index) => {
-                    grid_meta.fields.remove(index);
-                    Ok(Some(()))
+                    if grid_meta.fields[index].is_primary {
+                        Err(CollaborateError::can_not_delete_primary_field())
+                    } else {
+                        grid_meta.fields.remove(index);
+                        Ok(Some(()))
+                    }
                 }
             },
         )

+ 12 - 4
shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs

@@ -3,7 +3,7 @@ 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,
+    GroupConfigurationRevision, GroupConfigurationsByFieldId, LayoutRevision,
 };
 use lib_ot::core::{Delta, DeltaBuilder, EmptyAttributes, OperationTransform};
 use std::sync::Arc;
@@ -25,8 +25,8 @@ impl std::ops::Deref for GridViewRevisionPad {
 impl GridViewRevisionPad {
     // For the moment, the view_id is equal to grid_id. The grid_id represents the database id.
     // A database can be referenced by multiple views.
-    pub fn new(grid_id: String, view_id: String) -> Self {
-        let view = Arc::new(GridViewRevision::new(grid_id, view_id));
+    pub fn new(grid_id: String, view_id: String, layout: LayoutRevision) -> Self {
+        let view = Arc::new(GridViewRevision::new(grid_id, view_id, layout));
         let json = serde_json::to_string(&view).unwrap();
         let delta = DeltaBuilder::new().insert(&json).build();
         Self { view, delta }
@@ -34,7 +34,11 @@ impl GridViewRevisionPad {
 
     pub fn from_delta(view_id: &str, delta: Delta) -> CollaborateResult<Self> {
         if delta.is_empty() {
-            return Ok(GridViewRevisionPad::new(view_id.to_owned(), view_id.to_owned()));
+            return Ok(GridViewRevisionPad::new(
+                view_id.to_owned(),
+                view_id.to_owned(),
+                LayoutRevision::Table,
+            ));
         }
         let s = delta.content()?;
         let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| {
@@ -163,6 +167,10 @@ impl GridViewRevisionPad {
         make_grid_view_rev_json_str(&self.view)
     }
 
+    pub fn layout(&self) -> LayoutRevision {
+        self.layout.clone()
+    }
+
     fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridViewRevisionChangeset>>
     where
         F: FnOnce(&mut GridViewRevision) -> CollaborateResult<Option<()>>,

+ 9 - 7
shared-lib/flowy-sync/src/errors.rs

@@ -1,7 +1,7 @@
 use std::{fmt, fmt::Debug};
 use strum_macros::Display;
 
-macro_rules! static_doc_error {
+macro_rules! static_error {
     ($name:ident, $status:expr) => {
         #[allow(non_snake_case, missing_docs)]
         pub fn $name() -> CollaborateError {
@@ -34,12 +34,13 @@ impl CollaborateError {
         self
     }
 
-    static_doc_error!(internal, ErrorCode::InternalError);
-    static_doc_error!(undo, ErrorCode::UndoFail);
-    static_doc_error!(redo, ErrorCode::RedoFail);
-    static_doc_error!(out_of_bound, ErrorCode::OutOfBound);
-    static_doc_error!(record_not_found, ErrorCode::RecordNotFound);
-    static_doc_error!(revision_conflict, ErrorCode::RevisionConflict);
+    static_error!(internal, ErrorCode::InternalError);
+    static_error!(undo, ErrorCode::UndoFail);
+    static_error!(redo, ErrorCode::RedoFail);
+    static_error!(out_of_bound, ErrorCode::OutOfBound);
+    static_error!(record_not_found, ErrorCode::RecordNotFound);
+    static_error!(revision_conflict, ErrorCode::RevisionConflict);
+    static_error!(can_not_delete_primary_field, ErrorCode::CannotDeleteThePrimaryField);
 }
 
 impl fmt::Display for CollaborateError {
@@ -57,6 +58,7 @@ pub enum ErrorCode {
     OutOfBound = 202,
     RevisionConflict = 203,
     RecordNotFound = 300,
+    CannotDeleteThePrimaryField = 301,
     InternalError = 1000,
 }