Browse Source

chore: refactor group gen process

appflowy 2 years ago
parent
commit
f192f89ebb
22 changed files with 554 additions and 662 deletions
  1. 7 0
      frontend/app_flowy/assets/images/grid/setting/group.svg
  2. 2 1
      frontend/app_flowy/assets/translations/en.json
  3. 1 0
      frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart
  4. 10 0
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
  5. 27 0
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
  6. 4 0
      frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs
  7. 3 0
      frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs
  8. 75 96
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  9. 119 89
      frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
  10. 20 18
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  11. 17 19
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
  12. 80 0
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs
  13. 2 0
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/mod.rs
  14. 10 18
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
  15. 10 18
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
  16. 23 7
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
  17. 5 3
      frontend/rust-lib/flowy-grid/src/services/group/entities.rs
  18. 0 361
      frontend/rust-lib/flowy-grid/src/services/group/group_service.rs
  19. 113 0
      frontend/rust-lib/flowy-grid/src/services/group/group_util.rs
  20. 2 2
      frontend/rust-lib/flowy-grid/src/services/group/mod.rs
  21. 8 8
      frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs
  22. 16 22
      shared-lib/flowy-grid-data-model/src/revision/group_rev.rs

+ 7 - 0
frontend/app_flowy/assets/images/grid/setting/group.svg

@@ -0,0 +1,7 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10 2H13C13.5523 2 14 2.44772 14 3V6" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M6 2H3C2.44772 2 2 2.44772 2 3V6" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M6 14H3C2.44772 14 2 13.5523 2 13V10" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 14H13C13.5523 14 14 13.5523 14 13V10" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
+<rect x="6" y="6" width="4" height="4" rx="1" stroke="#333333"/>
+</svg>

+ 2 - 1
frontend/app_flowy/assets/translations/en.json

@@ -160,7 +160,8 @@
     "settings": {
       "filter": "Filter",
       "sortBy": "Sort by",
-      "Properties": "Properties"
+      "Properties": "Properties",
+      "group": "Group"
     },
     "field": {
       "hide": "Hide",

+ 1 - 0
frontend/app_flowy/lib/plugins/board/application/toolbar/board_setting_bloc.dart

@@ -43,4 +43,5 @@ class BoardSettingState with _$BoardSettingState {
 
 enum BoardSettingAction {
   properties,
+  groups,
 }

+ 10 - 0
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart

@@ -95,6 +95,12 @@ class BoardSettingList extends StatelessWidget {
                     fieldCache: settingContext.fieldCache)
                 .show(context);
             break;
+          case BoardSettingAction.groups:
+            GridPropertyList(
+                    gridId: settingContext.viewId,
+                    fieldCache: settingContext.fieldCache)
+                .show(context);
+            break;
         }
       },
     );
@@ -156,6 +162,8 @@ extension _GridSettingExtension on BoardSettingAction {
     switch (this) {
       case BoardSettingAction.properties:
         return 'grid/setting/properties';
+      case BoardSettingAction.groups:
+        return 'grid/setting/group';
     }
   }
 
@@ -163,6 +171,8 @@ extension _GridSettingExtension on BoardSettingAction {
     switch (this) {
       case BoardSettingAction.properties:
         return LocaleKeys.grid_settings_Properties.tr();
+      case BoardSettingAction.groups:
+        return LocaleKeys.grid_settings_group.tr();
     }
   }
 }

+ 27 - 0
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart

@@ -0,0 +1,27 @@
+import 'package:flowy_infra/theme.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class GridGroupList extends StatelessWidget {
+  const GridGroupList({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+
+class _GridGroupCell extends StatelessWidget {
+  const _GridGroupCell({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+
+    // final checkmark = field.visibility
+    //     ? svgWidget('home/show', color: theme.iconColor)
+    //     : svgWidget('home/hide', color: theme.iconColor);
+
+    return Container();
+  }
+}

+ 4 - 0
frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs

@@ -91,6 +91,9 @@ pub struct GroupPB {
 
     #[pb(index = 5)]
     pub is_default: bool,
+
+    #[pb(index = 6)]
+    pub is_visible: bool,
 }
 
 impl std::convert::From<Group> for GroupPB {
@@ -101,6 +104,7 @@ impl std::convert::From<Group> for GroupPB {
             desc: group.name,
             rows: group.rows,
             is_default: group.is_default,
+            is_visible: group.is_visible,
         }
     }
 }

+ 3 - 0
frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs

@@ -133,6 +133,9 @@ pub struct GroupViewChangesetPB {
     #[pb(index = 2)]
     pub inserted_groups: Vec<InsertedGroupPB>,
 
+    #[pb(index = 2)]
+    pub new_groups: Vec<GroupPB>,
+
     #[pb(index = 3)]
     pub deleted_groups: Vec<String>,
 

+ 75 - 96
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -7,7 +7,7 @@ use crate::entities::{
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
 use crate::services::group::{
-    make_group_controller, GroupConfigurationReader, GroupConfigurationWriter, GroupController, GroupService,
+    make_group_controller, GroupConfigurationReader, GroupConfigurationWriter, GroupController, MoveGroupRowContext,
 };
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
@@ -20,8 +20,6 @@ use flowy_sync::entities::revision::Revision;
 use lib_infra::future::{wrap_future, AFFuture, FutureResult};
 use std::collections::HashMap;
 use std::future::Future;
-
-use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
@@ -87,18 +85,16 @@ impl GridViewRevisionEditor {
     }
 
     pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
-        match params.group_id.as_ref() {
-            None => {}
-            Some(group_id) => {
-                self.group_service
-                    .write()
-                    .await
-                    .will_create_row(row_rev, group_id, |field_id| {
-                        self.field_delegate.get_field_rev(&field_id)
-                    })
-                    .await;
-            }
+        if params.group_id.is_none() {
+            return;
         }
+        let group_id = params.group_id.as_ref().unwrap();
+        let _ = self
+            .mut_group_controller(|group_controller, field_rev| {
+                group_controller.will_create_row(row_rev, &field_rev, group_id);
+                Ok(())
+            })
+            .await;
     }
 
     pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
@@ -123,58 +119,29 @@ impl GridViewRevisionEditor {
 
     pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) {
         // Send the group notification if the current view has groups;
-        let group_field_id = self.group_controller.read().await.field_id().to_owned();
-        let field_rev = self.field_delegate.get_field_rev(&group_field_id).await;
-        field_rev.and_then(|field_rev| {
-            if let Some(changesets) = self
-                .group_controller
-                .write()
-                .await
-                .did_delete_row(row_rev, &field_rev)
-                .await
-            {
-                for changeset in changesets {
-                    self.notify_did_update_group(changeset).await;
-                }
-            }
-            None
-        });
-    }
-
-    pub(crate) async fn did_update_row(&self, row_rev: &RowRevision) {
-        let changeset = self
-            .mut_group_controller(|group_controller, field_rev| async {
-                group_controller.did_update_row(row_rev, &field_rev).await
-            })
-            .await?;
+        let changesets = self
+            .mut_group_controller(|group_controller, field_rev| group_controller.did_delete_row(row_rev, &field_rev))
+            .await;
 
-        if let Some(changeset) = changeset {
+        if let Some(changesets) = changesets {
             for changeset in changesets {
                 self.notify_did_update_group(changeset).await;
             }
         }
     }
 
-    async fn mut_group_controller<F, O, T>(&self, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut Box<dyn GroupController>, Arc<FieldRevision>) -> O,
-        O: Future<Output = Option<T>> + Send + Sync + 'static,
-    {
-        let group_field_id = self.group_controller.read().await.field_id().to_owned();
-        match self.field_delegate.get_field_rev(&group_field_id).await {
-            None => None,
-            Some(field_rev) => {
-                let mut write_guard = self.group_controller.write().await;
-                Some(f(&mut write_guard, field_rev).await)
+    pub(crate) async fn did_update_row(&self, row_rev: &RowRevision) {
+        let changesets = self
+            .mut_group_controller(|group_controller, field_rev| group_controller.did_update_row(row_rev, &field_rev))
+            .await;
+
+        if let Some(changesets) = changesets {
+            for changeset in changesets {
+                self.notify_did_update_group(changeset).await;
             }
         }
     }
 
-    async fn get_group_field_rev(&self) -> Option<Arc<FieldRevision>> {
-        let group_field_id = self.group_controller.read().await.field_id().to_owned();
-        self.field_delegate.get_field_rev(&group_field_id).await
-    }
-
     pub(crate) async fn move_group_row(
         &self,
         row_rev: &RowRevision,
@@ -182,54 +149,38 @@ impl GridViewRevisionEditor {
         to_group_id: &str,
         to_row_id: Option<String>,
     ) -> Vec<GroupChangesetPB> {
-        match self
-            .group_service
-            .write()
-            .await
-            .move_group_row(row_rev, row_changeset, to_group_id, to_row_id, |field_id| {
-                self.field_delegate.get_field_rev(&field_id)
+        let changesets = self
+            .mut_group_controller(|group_controller, field_rev| {
+                let move_row_context = MoveGroupRowContext {
+                    row_rev,
+                    row_changeset,
+                    field_rev: field_rev.as_ref(),
+                    to_group_id,
+                    to_row_id,
+                };
+
+                let changesets = group_controller.move_group_row(move_row_context)?;
+                Ok(changesets)
             })
-            .await
-        {
-            None => vec![],
-            Some(changesets) => changesets,
-        }
+            .await;
+
+        changesets.unwrap_or_default()
     }
     /// Only call once after grid view editor initialized
     #[tracing::instrument(level = "trace", skip(self))]
     pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
-        let groups = if !self.did_load_group.load(Ordering::SeqCst) {
-            self.did_load_group.store(true, Ordering::SeqCst);
-            let field_revs = self.field_delegate.get_field_revs().await;
-            let row_revs = self.row_delegate.gv_row_revs().await;
-
-            match self
-                .group_service
-                .write()
-                .await
-                .load_groups(&field_revs, row_revs)
-                .await
-            {
-                None => vec![],
-                Some(groups) => groups,
-            }
-        } else {
-            self.group_service.read().await.groups().await
-        };
-
+        let groups = self.group_controller.read().await.groups();
         tracing::trace!("Number of groups: {}", groups.len());
         Ok(groups.into_iter().map(GroupPB::from).collect())
     }
 
     pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
         let _ = self
-            .group_service
+            .group_controller
             .write()
             .await
-            .move_group(&params.from_group_id, &params.to_group_id)
-            .await?;
-
-        match self.group_service.read().await.get_group(&params.from_group_id).await {
+            .move_group(&params.from_group_id, &params.to_group_id)?;
+        match self.group_controller.read().await.get_group(&params.from_group_id) {
             None => {}
             Some((index, group)) => {
                 let inserted_group = InsertedGroupPB {
@@ -242,6 +193,7 @@ impl GridViewRevisionEditor {
                     inserted_groups: vec![inserted_group],
                     deleted_groups: vec![params.from_group_id.clone()],
                     update_groups: vec![],
+                    new_groups: vec![],
                 };
 
                 self.notify_did_update_view(changeset).await;
@@ -296,7 +248,7 @@ impl GridViewRevisionEditor {
     #[tracing::instrument(level = "trace", skip_all, err)]
     pub(crate) async fn did_update_field(&self, field_id: &str) -> FlowyResult<()> {
         if let Some(field_rev) = self.field_delegate.get_field_rev(field_id).await {
-            match self.group_service.write().await.did_update_field(&field_rev).await? {
+            match self.group_controller.write().await.did_update_field(&field_rev)? {
                 None => {}
                 Some(changeset) => {
                     self.notify_did_update_view(changeset).await;
@@ -331,6 +283,36 @@ impl GridViewRevisionEditor {
         }
         Ok(())
     }
+
+    async fn mut_group_controller<F, T>(&self, f: F) -> Option<T>
+    where
+        F: FnOnce(&mut Box<dyn GroupController>, Arc<FieldRevision>) -> FlowyResult<T>,
+    {
+        let group_field_id = self.group_controller.read().await.field_id().to_owned();
+        match self.field_delegate.get_field_rev(&group_field_id).await {
+            None => None,
+            Some(field_rev) => {
+                let mut write_guard = self.group_controller.write().await;
+                f(&mut write_guard, field_rev).ok()
+            }
+        }
+    }
+
+    #[allow(dead_code)]
+    async fn async_mut_group_controller<F, O, T>(&self, f: F) -> Option<T>
+    where
+        F: FnOnce(Arc<RwLock<Box<dyn GroupController>>>, Arc<FieldRevision>) -> O,
+        O: Future<Output = FlowyResult<T>> + Sync + 'static,
+    {
+        let group_field_id = self.group_controller.read().await.field_id().to_owned();
+        match self.field_delegate.get_field_rev(&group_field_id).await {
+            None => None,
+            Some(field_rev) => {
+                let _write_guard = self.group_controller.write().await;
+                f(self.group_controller.clone(), field_rev).await.ok()
+            }
+        }
+    }
 }
 
 async fn apply_change(
@@ -371,10 +353,7 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
 struct GroupConfigurationReaderImpl(Arc<RwLock<GridViewRevisionPad>>);
 
 impl GroupConfigurationReader for GroupConfigurationReaderImpl {
-    fn get_group_configuration(
-        &self,
-        field_rev: Arc<FieldRevision>,
-    ) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
+    fn get_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
         let view_pad = self.0.clone();
         wrap_future(async move {
             let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?;
@@ -395,7 +374,7 @@ struct GroupConfigurationWriterImpl {
 }
 
 impl GroupConfigurationWriter for GroupConfigurationWriterImpl {
-    fn save_group_configuration(
+    fn save_configuration(
         &self,
         field_id: &str,
         field_type: FieldTypeRevision,

+ 119 - 89
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs

@@ -1,24 +1,22 @@
 use crate::entities::{GroupPB, GroupViewChangesetPB, InsertedGroupPB};
-use crate::services::group::{default_group_configuration, Group};
+use crate::services::group::{default_group_configuration, GeneratedGroup, Group};
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
     FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRevision,
 };
 use indexmap::IndexMap;
 use lib_infra::future::AFFuture;
+use std::collections::HashMap;
 use std::fmt::Formatter;
 use std::marker::PhantomData;
 use std::sync::Arc;
 
 pub trait GroupConfigurationReader: Send + Sync + 'static {
-    fn get_group_configuration(
-        &self,
-        field_rev: Arc<FieldRevision>,
-    ) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
+    fn get_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
 }
 
 pub trait GroupConfigurationWriter: Send + Sync + 'static {
-    fn save_group_configuration(
+    fn save_configuration(
         &self,
         field_id: &str,
         field_type: FieldTypeRevision,
@@ -26,7 +24,7 @@ pub trait GroupConfigurationWriter: Send + Sync + 'static {
     ) -> AFFuture<FlowyResult<()>>;
 }
 
-impl<T> std::fmt::Display for GenericGroupConfiguration<T> {
+impl<T> std::fmt::Display for GroupContext<T> {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         self.groups_map.iter().for_each(|(_, group)| {
             let _ = f.write_fmt(format_args!("Group:{} has {} rows \n", group.id, group.rows.len()));
@@ -39,7 +37,7 @@ impl<T> std::fmt::Display for GenericGroupConfiguration<T> {
     }
 }
 
-pub struct GenericGroupConfiguration<C> {
+pub struct GroupContext<C> {
     view_id: String,
     pub configuration: Arc<GroupConfigurationRevision>,
     configuration_content: PhantomData<C>,
@@ -50,7 +48,7 @@ pub struct GenericGroupConfiguration<C> {
     writer: Arc<dyn GroupConfigurationWriter>,
 }
 
-impl<C> GenericGroupConfiguration<C>
+impl<C> GroupContext<C>
 where
     C: GroupConfigurationContentSerde,
 {
@@ -67,16 +65,17 @@ where
             field_id: field_rev.id.clone(),
             name: format!("No {}", field_rev.name),
             is_default: true,
+            is_visible: true,
             rows: vec![],
-            content: "".to_string(),
+            filter_content: "".to_string(),
         };
-        let configuration = match reader.get_group_configuration(field_rev.clone()).await {
+        let configuration = match reader.get_configuration(field_rev.clone()).await {
             None => {
-                let default_group_configuration = default_group_configuration(&field_rev);
+                let default_configuration = default_group_configuration(&field_rev);
                 writer
-                    .save_group_configuration(&field_rev.id, field_rev.ty, default_group_configuration.clone())
+                    .save_configuration(&field_rev.id, field_rev.ty, default_configuration.clone())
                     .await?;
-                Arc::new(default_group_configuration)
+                Arc::new(default_configuration)
             }
             Some(configuration) => configuration,
         };
@@ -134,47 +133,100 @@ where
         }
     }
 
-    pub(crate) fn merge_groups(&mut self, groups: Vec<Group>) -> FlowyResult<Option<GroupViewChangesetPB>> {
+    pub(crate) fn init_group_revs(
+        &mut self,
+        generated_groups: Vec<GeneratedGroup>,
+    ) -> FlowyResult<Option<GroupViewChangesetPB>> {
+        let mut new_groups = vec![];
+        let mut filter_content_map = HashMap::new();
+        generated_groups.into_iter().for_each(|generate_group| {
+            filter_content_map.insert(generate_group.group_rev.id.clone(), generate_group.filter_content);
+            new_groups.push(generate_group.group_rev);
+        });
+
         let MergeGroupResult {
-            groups,
-            inserted_groups,
-            updated_groups,
-        } = merge_groups(&self.configuration.groups, groups);
+            mut all_group_revs,
+            new_group_revs,
+            updated_group_revs: _,
+            deleted_group_revs,
+        } = merge_groups(&self.configuration.groups, new_groups);
 
-        let group_revs = groups
+        let deleted_group_ids = deleted_group_revs
             .iter()
-            .map(|group| GroupRevision::new(group.id.clone(), group.name.clone()))
-            .collect::<Vec<GroupRevision>>();
+            .map(|group_rev| group_rev.id)
+            .collect::<Vec<String>>();
 
-        self.mut_configuration(move |configuration| {
+        self.mut_configuration(|configuration| {
             let mut is_changed = false;
-            for new_group_rev in group_revs {
+            if !deleted_group_ids.is_empty() {
+                configuration
+                    .groups
+                    .retain(|group| !deleted_group_ids.contains(&group.id));
+                is_changed = true;
+            }
+
+            for mut group_rev in &mut all_group_revs {
                 match configuration
                     .groups
                     .iter()
-                    .position(|group_rev| group_rev.id == new_group_rev.id)
+                    .position(|old_group_rev| old_group_rev.id == group_rev.id)
                 {
                     None => {
-                        configuration.groups.push(new_group_rev);
+                        configuration.groups.push(group_rev.clone());
                         is_changed = true;
                     }
                     Some(pos) => {
-                        let removed_group = configuration.groups.remove(pos);
-                        if removed_group != new_group_rev {
+                        let mut old_group = configuration.groups.remove(pos);
+                        // Update the group configuration base on the GroupRevision
+                        group_rev.visible = old_group.visible;
+
+                        // Take the GroupRevision if the name has changed
+                        if is_group_changed(&group_rev, &old_group) {
+                            old_group.name = group_rev.name.clone();
                             is_changed = true;
+                            configuration.groups.insert(pos, old_group);
                         }
-                        configuration.groups.insert(pos, new_group_rev);
                     }
                 }
             }
             is_changed
         })?;
 
-        groups.into_iter().for_each(|group| {
-            self.groups_map.insert(group.id.clone(), group);
+        debug_assert_eq!(filter_content_map.len(), all_group_revs.len());
+        all_group_revs.into_iter().for_each(|group_rev| {
+            if let Some(filter_content) = filter_content_map.get(&group_rev.id) {
+                let group = Group::new(
+                    group_rev.id,
+                    self.field_rev.id.clone(),
+                    group_rev.name,
+                    filter_content.clone(),
+                );
+                self.groups_map.insert(group.id.clone(), group);
+            }
         });
 
-        let changeset = make_group_view_changeset(self.view_id.clone(), inserted_groups, updated_groups);
+        let new_groups = new_group_revs
+            .into_iter()
+            .map(|group_rev| {
+                if let Some(filter_content) = filter_content_map.get(&group_rev.id) {
+                    let group = Group::new(
+                        group_rev.id,
+                        self.field_rev.id.clone(),
+                        group_rev.name,
+                        filter_content.clone(),
+                    );
+                    GroupPB::from(group)
+                }
+            })
+            .collect();
+
+        let changeset = GroupViewChangesetPB {
+            view_id,
+            new_groups,
+            deleted_groups: deleted_group_ids,
+            update_groups: vec![],
+            inserted_groups: vec![],
+        };
         tracing::trace!("Group changeset: {:?}", changeset);
         if changeset.is_empty() {
             Ok(None)
@@ -221,10 +273,7 @@ where
         let field_id = self.field_rev.id.clone();
         let field_type = self.field_rev.ty;
         tokio::spawn(async move {
-            match writer
-                .save_group_configuration(&field_id, field_type, configuration)
-                .await
-            {
+            match writer.save_configuration(&field_id, field_type, configuration).await {
                 Ok(_) => {}
                 Err(e) => {
                     tracing::error!("Save group configuration failed: {}", e);
@@ -260,82 +309,63 @@ where
     }
 }
 
-fn merge_groups(old_groups: &[GroupRevision], groups: Vec<Group>) -> MergeGroupResult {
+fn merge_groups(old_groups: &[GroupRevision], new_groups: Vec<GroupRevision>) -> MergeGroupResult {
     let mut merge_result = MergeGroupResult::new();
     if old_groups.is_empty() {
-        merge_result.groups = groups;
+        merge_result.all_group_revs = new_groups;
         return merge_result;
     }
 
     // group_map is a helper map is used to filter out the new groups.
-    let mut group_map: IndexMap<String, Group> = IndexMap::new();
-    groups.into_iter().for_each(|group| {
-        group_map.insert(group.id.clone(), group);
+    let mut new_group_map: IndexMap<String, GroupRevision> = IndexMap::new();
+    new_groups.into_iter().for_each(|group_rev| {
+        new_group_map.insert(group_rev.id.clone(), group_rev);
     });
 
     // The group is ordered in old groups. Add them before adding the new groups
-    for group_rev in old_groups {
-        if let Some(group) = group_map.remove(&group_rev.id) {
-            if group.name == group_rev.name {
-                merge_result.add_group(group);
-            } else {
-                merge_result.add_updated_group(group);
+    for old in old_groups {
+        if let Some(new) = new_group_map.remove(&old.id) {
+            merge_result.all_group_revs.push(new.clone());
+            if is_group_changed(&new, old) {
+                merge_result.updated_group_revs.push(new);
             }
+        } else {
+            merge_result.deleted_group_revs.push(old.clone());
         }
     }
 
     // Find out the new groups
-    group_map
-        .into_values()
-        .enumerate()
-        .for_each(|(index, group)| merge_result.add_insert_group(index, group));
-
+    let new_groups = new_group_map.into_values();
+    for (_, group) in new_groups.into_iter().enumerate() {
+        merge_result.all_group_revs.push(group.clone());
+        merge_result.new_group_revs.push(group);
+    }
     merge_result
 }
 
+fn is_group_changed(new: &GroupRevision, old: &GroupRevision) -> bool {
+    if new.name != old.name {
+        return true;
+    }
+
+    false
+}
+
 struct MergeGroupResult {
-    groups: Vec<Group>,
-    inserted_groups: Vec<InsertedGroupPB>,
-    updated_groups: Vec<Group>,
+    // Contains the new groups and the updated groups
+    all_group_revs: Vec<GroupRevision>,
+    new_group_revs: Vec<GroupRevision>,
+    updated_group_revs: Vec<GroupRevision>,
+    deleted_group_revs: Vec<GroupRevision>,
 }
 
 impl MergeGroupResult {
     fn new() -> Self {
         Self {
-            groups: vec![],
-            inserted_groups: vec![],
-            updated_groups: vec![],
+            all_group_revs: vec![],
+            new_group_revs: vec![],
+            updated_group_revs: vec![],
+            deleted_group_revs: vec![],
         }
     }
-
-    fn add_updated_group(&mut self, group: Group) {
-        self.groups.push(group.clone());
-        self.updated_groups.push(group);
-    }
-
-    fn add_group(&mut self, group: Group) {
-        self.groups.push(group);
-    }
-
-    fn add_insert_group(&mut self, index: usize, group: Group) {
-        self.groups.push(group.clone());
-        let inserted_group = InsertedGroupPB {
-            group: GroupPB::from(group),
-            index: index as i32,
-        };
-        self.inserted_groups.push(inserted_group);
-    }
-}
-
-fn make_group_view_changeset(
-    view_id: String,
-    inserted_groups: Vec<InsertedGroupPB>,
-    updated_group: Vec<Group>,
-) -> GroupViewChangesetPB {
-    GroupViewChangesetPB {
-        view_id,
-        inserted_groups,
-        deleted_groups: vec![],
-        update_groups: updated_group.into_iter().map(GroupPB::from).collect(),
-    }
 }

+ 20 - 18
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -1,11 +1,11 @@
 use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
 use crate::services::cell::{decode_any_cell_data, CellBytesParser};
 use crate::services::group::action::GroupAction;
-use crate::services::group::configuration::GenericGroupConfiguration;
+use crate::services::group::configuration::GroupContext;
 use crate::services::group::entities::Group;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
-    FieldRevision, GroupConfigurationContentSerde, RowChangeset, RowRevision, TypeOptionDataDeserializer,
+    FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer,
 };
 
 use std::marker::PhantomData;
@@ -19,14 +19,19 @@ pub trait GroupController: GroupControllerSharedOperation + Send + Sync {
 }
 
 pub trait GroupGenerator {
-    type ConfigurationType;
+    type Context;
     type TypeOptionType;
 
     fn generate_groups(
         field_id: &str,
-        configuration: &Self::ConfigurationType,
+        group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<Group>;
+    ) -> Vec<GeneratedGroup>;
+}
+
+pub struct GeneratedGroup {
+    pub group_rev: GroupRevision,
+    pub filter_content: String,
 }
 
 pub struct MoveGroupRowContext<'a> {
@@ -43,7 +48,7 @@ pub trait GroupControllerSharedOperation: Send + Sync {
     fn field_id(&self) -> &str;
     fn groups(&self) -> Vec<Group>;
     fn get_group(&self, group_id: &str) -> Option<(usize, Group)>;
-    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>>;
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
     fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
     fn did_update_row(
         &mut self,
@@ -69,7 +74,7 @@ pub trait GroupControllerSharedOperation: Send + Sync {
 pub struct GenericGroupController<C, T, G, P> {
     pub field_id: String,
     pub type_option: Option<T>,
-    pub configuration: GenericGroupConfiguration<C>,
+    pub configuration: GroupContext<C>,
     group_action_phantom: PhantomData<G>,
     cell_parser_phantom: PhantomData<P>,
 }
@@ -78,16 +83,13 @@ impl<C, T, G, P> GenericGroupController<C, T, G, P>
 where
     C: GroupConfigurationContentSerde,
     T: TypeOptionDataDeserializer,
-    G: GroupGenerator<ConfigurationType = GenericGroupConfiguration<C>, TypeOptionType = T>,
+    G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
 {
-    pub async fn new(
-        field_rev: &Arc<FieldRevision>,
-        mut configuration: GenericGroupConfiguration<C>,
-    ) -> FlowyResult<Self> {
+    pub async fn new(field_rev: &Arc<FieldRevision>, mut configuration: GroupContext<C>) -> FlowyResult<Self> {
         let field_type_rev = field_rev.ty;
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
         let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
-        let _ = configuration.merge_groups(groups)?;
+        let _ = configuration.init_group_revs(groups)?;
 
         Ok(Self {
             field_id: field_rev.id.clone(),
@@ -171,7 +173,7 @@ where
     P: CellBytesParser,
     C: GroupConfigurationContentSerde,
     T: TypeOptionDataDeserializer,
-    G: GroupGenerator<ConfigurationType = GenericGroupConfiguration<C>, TypeOptionType = T>,
+    G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
 
     Self: GroupAction<CellDataType = P::Object>,
 {
@@ -189,14 +191,14 @@ where
     }
 
     #[tracing::instrument(level = "trace", skip_all, fields(row_count=%row_revs.len(), group_result))]
-    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
         for row_rev in row_revs {
             if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
                 let mut grouped_rows: Vec<GroupedRow> = vec![];
                 let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
                 let cell_data = cell_bytes.parser::<P>()?;
                 for group in self.configuration.concrete_groups() {
-                    if self.can_group(&group.content, &cell_data) {
+                    if self.can_group(&group.filter_content, &cell_data) {
                         grouped_rows.push(GroupedRow {
                             row: row_rev.into(),
                             group_id: group.id.clone(),
@@ -219,7 +221,7 @@ where
         }
 
         tracing::Span::current().record("group_result", &format!("{},", self.configuration,).as_str());
-        Ok(self.groups())
+        Ok(())
     }
 
     fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
@@ -274,7 +276,7 @@ where
         let field_type_rev = field_rev.ty;
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
         let groups = G::generate_groups(&field_rev.id, &self.configuration, &type_option);
-        let changeset = self.configuration.merge_groups(groups)?;
+        let changeset = self.configuration.init_group_revs(groups)?;
         Ok(changeset)
     }
 }

+ 17 - 19
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs

@@ -1,13 +1,14 @@
 use crate::entities::GroupChangesetPB;
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
 use crate::services::group::action::GroupAction;
-use crate::services::group::configuration::GenericGroupConfiguration;
+use crate::services::group::configuration::GroupContext;
 use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
 use crate::services::group::entities::Group;
 
-use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowRevision};
+use crate::services::group::GeneratedGroup;
+use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision};
 
 pub type CheckboxGroupController = GenericGroupController<
     CheckboxGroupConfigurationRevision,
@@ -16,7 +17,7 @@ pub type CheckboxGroupController = GenericGroupController<
     CheckboxCellDataParser,
 >;
 
-pub type CheckboxGroupConfiguration = GenericGroupConfiguration<CheckboxGroupConfigurationRevision>;
+pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision>;
 
 impl GroupAction for CheckboxGroupController {
     type CellDataType = CheckboxCellData;
@@ -49,26 +50,23 @@ impl GroupController for CheckboxGroupController {
 
 pub struct CheckboxGroupGenerator();
 impl GroupGenerator for CheckboxGroupGenerator {
-    type ConfigurationType = CheckboxGroupConfiguration;
+    type Context = CheckboxGroupContext;
     type TypeOptionType = CheckboxTypeOptionPB;
 
     fn generate_groups(
         field_id: &str,
-        _configuration: &Self::ConfigurationType,
-        _type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<Group> {
-        let check_group = Group::new(
-            "true".to_string(),
-            field_id.to_owned(),
-            "".to_string(),
-            CHECK.to_string(),
-        );
-        let uncheck_group = Group::new(
-            "false".to_string(),
-            field_id.to_owned(),
-            "".to_string(),
-            UNCHECK.to_string(),
-        );
+        group_ctx: &Self::Context,
+        type_option: &Option<Self::TypeOptionType>,
+    ) -> Vec<GeneratedGroup> {
+        let check_group = GeneratedGroup {
+            group_rev: GroupRevision::new("true".to_string(), CHECK.to_string()),
+            filter_content: "".to_string(),
+        };
+
+        let uncheck_group = GeneratedGroup {
+            group_rev: GroupRevision::new("false".to_string(), UNCHECK.to_string()),
+            filter_content: "".to_string(),
+        };
         vec![check_group, uncheck_group]
     }
 }

+ 80 - 0
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs

@@ -0,0 +1,80 @@
+use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, RowPB};
+use crate::services::group::{Group, GroupController, GroupControllerSharedOperation, MoveGroupRowContext};
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
+use std::sync::Arc;
+
+pub struct DefaultGroupController {
+    pub field_id: String,
+    pub group: Group,
+}
+
+const DEFAULT_GROUP_CONTROLLER: &str = "DefaultGroupController";
+
+impl DefaultGroupController {
+    pub fn new(field_rev: &Arc<FieldRevision>) -> Self {
+        let group = Group::new(
+            DEFAULT_GROUP_CONTROLLER.to_owned(),
+            field_rev.id.clone(),
+            "Oops".to_owned(),
+            "".to_owned(),
+        );
+        Self {
+            field_id: field_rev.id.clone(),
+            group,
+        }
+    }
+}
+
+impl GroupControllerSharedOperation for DefaultGroupController {
+    fn field_id(&self) -> &str {
+        &self.field_id
+    }
+
+    fn groups(&self) -> Vec<Group> {
+        vec![self.group.clone()]
+    }
+
+    fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
+        Some((0, self.group.clone()))
+    }
+
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
+        row_revs.iter().for_each(|row_rev| {
+            self.group.add_row(RowPB::from(row_rev));
+        });
+        Ok(())
+    }
+
+    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
+        Ok(())
+    }
+
+    fn did_update_row(
+        &mut self,
+        row_rev: &RowRevision,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<Vec<GroupChangesetPB>> {
+        todo!()
+    }
+
+    fn did_delete_row(
+        &mut self,
+        row_rev: &RowRevision,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<Vec<GroupChangesetPB>> {
+        todo!()
+    }
+
+    fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupChangesetPB>> {
+        todo!()
+    }
+
+    fn did_update_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
+        Ok(None)
+    }
+}
+
+impl GroupController for DefaultGroupController {
+    fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {}
+}

+ 2 - 0
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/mod.rs

@@ -1,5 +1,7 @@
 mod checkbox_controller;
+mod default_controller;
 mod select_option_controller;
 
 pub use checkbox_controller::*;
+pub use default_controller::*;
 pub use select_option_controller::*;

+ 10 - 18
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs

@@ -8,7 +8,10 @@ use crate::services::group::controller::{
 };
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
-use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
+use crate::services::group::GeneratedGroup;
+use flowy_grid_data_model::revision::{
+    FieldRevision, GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision,
+};
 
 // MultiSelect
 pub type MultiSelectGroupController = GenericGroupController<
@@ -28,7 +31,7 @@ impl GroupAction for MultiSelectGroupController {
     fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
         self.configuration.iter_mut_groups(|group| {
-            if let Some(changeset) = add_row(group, cell_data, row_rev) {
+            if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
@@ -38,7 +41,7 @@ impl GroupAction for MultiSelectGroupController {
     fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
         self.configuration.iter_mut_groups(|group| {
-            if let Some(changeset) = remove_row(group, cell_data, row_rev) {
+            if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
@@ -70,27 +73,16 @@ impl GroupController for MultiSelectGroupController {
 
 pub struct MultiSelectGroupGenerator();
 impl GroupGenerator for MultiSelectGroupGenerator {
-    type ConfigurationType = SelectOptionGroupConfiguration;
+    type Context = SelectOptionGroupContext;
     type TypeOptionType = MultiSelectTypeOptionPB;
     fn generate_groups(
         field_id: &str,
-        _configuration: &Self::ConfigurationType,
+        group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<Group> {
+    ) -> Vec<GeneratedGroup> {
         match type_option {
             None => vec![],
-            Some(type_option) => type_option
-                .options
-                .iter()
-                .map(|option| {
-                    Group::new(
-                        option.id.clone(),
-                        field_id.to_owned(),
-                        option.name.clone(),
-                        option.id.clone(),
-                    )
-                })
-                .collect(),
+            Some(type_option) => generate_select_option_groups(field_id, group_ctx, &type_option.options),
         }
     }
 }

+ 10 - 18
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs

@@ -9,7 +9,10 @@ use crate::services::group::controller::{
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
 
-use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
+use crate::services::group::GeneratedGroup;
+use flowy_grid_data_model::revision::{
+    FieldRevision, GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision,
+};
 
 // SingleSelect
 pub type SingleSelectGroupController = GenericGroupController<
@@ -28,7 +31,7 @@ impl GroupAction for SingleSelectGroupController {
     fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
         self.configuration.iter_mut_groups(|group| {
-            if let Some(changeset) = add_row(group, cell_data, row_rev) {
+            if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
@@ -38,7 +41,7 @@ impl GroupAction for SingleSelectGroupController {
     fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
         self.configuration.iter_mut_groups(|group| {
-            if let Some(changeset) = remove_row(group, cell_data, row_rev) {
+            if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
@@ -72,27 +75,16 @@ impl GroupController for SingleSelectGroupController {
 
 pub struct SingleSelectGroupGenerator();
 impl GroupGenerator for SingleSelectGroupGenerator {
-    type ConfigurationType = SelectOptionGroupConfiguration;
+    type Context = SelectOptionGroupContext;
     type TypeOptionType = SingleSelectTypeOptionPB;
     fn generate_groups(
         field_id: &str,
-        _configuration: &Self::ConfigurationType,
+        group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<Group> {
+    ) -> Vec<GeneratedGroup> {
         match type_option {
             None => vec![],
-            Some(type_option) => type_option
-                .options
-                .iter()
-                .map(|option| {
-                    Group::new(
-                        option.id.clone(),
-                        field_id.to_owned(),
-                        option.name.clone(),
-                        option.id.clone(),
-                    )
-                })
-                .collect(),
+            Some(type_option) => generate_select_option_groups(field_id, group_ctx, &type_option.options),
         }
     }
 }

+ 23 - 7
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs

@@ -1,15 +1,15 @@
 use crate::entities::{GroupChangesetPB, InsertedRowPB, RowPB};
 use crate::services::cell::insert_select_option_cell;
-use crate::services::field::SelectOptionCellDataPB;
-use crate::services::group::configuration::GenericGroupConfiguration;
-use crate::services::group::Group;
+use crate::services::field::{SelectOptionCellDataPB, SelectOptionPB};
+use crate::services::group::configuration::GroupContext;
+use crate::services::group::{GeneratedGroup, Group};
 
 use crate::services::group::controller::MoveGroupRowContext;
-use flowy_grid_data_model::revision::{RowRevision, SelectOptionGroupConfigurationRevision};
+use flowy_grid_data_model::revision::{GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision};
 
-pub type SelectOptionGroupConfiguration = GenericGroupConfiguration<SelectOptionGroupConfigurationRevision>;
+pub type SelectOptionGroupContext = GroupContext<SelectOptionGroupConfigurationRevision>;
 
-pub fn add_row(
+pub fn add_select_option_row(
     group: &mut Group,
     cell_data: &SelectOptionCellDataPB,
     row_rev: &RowRevision,
@@ -42,7 +42,7 @@ pub fn add_row(
     }
 }
 
-pub fn remove_row(
+pub fn remove_select_option_row(
     group: &mut Group,
     cell_data: &SelectOptionCellDataPB,
     row_rev: &RowRevision,
@@ -125,3 +125,19 @@ pub fn move_select_option_row(
         Some(changeset)
     }
 }
+
+pub fn generate_select_option_groups(
+    field_id: &str,
+    group_ctx: &SelectOptionGroupContext,
+    options: &[SelectOptionPB],
+) -> Vec<GeneratedGroup> {
+    let groups = options
+        .iter()
+        .map(|option| GeneratedGroup {
+            group_rev: GroupRevision::new(option.id.clone(), option.name.clone()),
+            filter_content: option.id.clone(),
+        })
+        .collect();
+
+    groups
+}

+ 5 - 3
frontend/rust-lib/flowy-grid/src/services/group/entities.rs

@@ -6,21 +6,23 @@ pub struct Group {
     pub field_id: String,
     pub name: String,
     pub is_default: bool,
+    pub is_visible: bool,
     pub(crate) rows: Vec<RowPB>,
 
     /// [content] is used to determine which group the cell belongs to.
-    pub content: String,
+    pub filter_content: String,
 }
 
 impl Group {
-    pub fn new(id: String, field_id: String, name: String, content: String) -> Self {
+    pub fn new(id: String, field_id: String, name: String, filter_content: String) -> Self {
         Self {
             id,
             field_id,
             is_default: false,
+            is_visible: true,
             name,
             rows: vec![],
-            content,
+            filter_content,
         }
     }
 

+ 0 - 361
frontend/rust-lib/flowy-grid/src/services/group/group_service.rs

@@ -1,361 +0,0 @@
-use crate::entities::{FieldType, GroupChangesetPB, GroupViewChangesetPB};
-use crate::services::group::configuration::GroupConfigurationReader;
-use crate::services::group::controller::{GroupController, MoveGroupRowContext};
-use crate::services::group::{
-    CheckboxGroupConfiguration, CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController,
-    SelectOptionGroupConfiguration, SingleSelectGroupController,
-};
-use flowy_error::FlowyResult;
-use flowy_grid_data_model::revision::{
-    CheckboxGroupConfigurationRevision, DateGroupConfigurationRevision, FieldRevision, GroupConfigurationRevision,
-    NumberGroupConfigurationRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
-    TextGroupConfigurationRevision, UrlGroupConfigurationRevision,
-};
-use std::future::Future;
-use std::sync::Arc;
-
-pub(crate) struct GroupService {
-    view_id: String,
-    configuration_reader: Arc<dyn GroupConfigurationReader>,
-    configuration_writer: Arc<dyn GroupConfigurationWriter>,
-    group_controller: Option<Box<dyn GroupController>>,
-}
-
-impl GroupService {
-    pub(crate) async fn new<R, W>(view_id: String, configuration_reader: R, configuration_writer: W) -> Self
-    where
-        R: GroupConfigurationReader,
-        W: GroupConfigurationWriter,
-    {
-        Self {
-            view_id,
-            configuration_reader: Arc::new(configuration_reader),
-            configuration_writer: Arc::new(configuration_writer),
-            group_controller: None,
-        }
-    }
-
-    pub(crate) async fn groups(&self) -> Vec<Group> {
-        self.group_controller
-            .as_ref()
-            .map(|group_controller| group_controller.groups())
-            .unwrap_or_default()
-    }
-
-    pub(crate) async fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
-        self.group_controller
-            .as_ref()
-            .and_then(|group_controller| group_controller.get_group(group_id))
-    }
-
-    pub(crate) async fn load_groups(
-        &mut self,
-        field_revs: &[Arc<FieldRevision>],
-        row_revs: Vec<Arc<RowRevision>>,
-    ) -> Option<Vec<Group>> {
-        let field_rev = find_group_field(field_revs)?;
-        let field_type: FieldType = field_rev.ty.into();
-
-        let mut group_controller = self.make_group_controller(&field_type, &field_rev).await.ok()??;
-        let groups = match group_controller.fill_groups(&row_revs, &field_rev) {
-            Ok(groups) => groups,
-            Err(e) => {
-                tracing::error!("Fill groups failed:{:?}", e);
-                vec![]
-            }
-        };
-        self.group_controller = Some(group_controller);
-        Some(groups)
-    }
-
-    pub(crate) async fn will_create_row<F, O>(&mut self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F)
-    where
-        F: FnOnce(String) -> O,
-        O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
-    {
-        if let Some(group_controller) = self.group_controller.as_mut() {
-            let field_id = group_controller.field_id().to_owned();
-            match get_field_fn(field_id).await {
-                None => {}
-                Some(field_rev) => {
-                    group_controller.will_create_row(row_rev, &field_rev, group_id);
-                }
-            }
-        }
-    }
-
-    pub(crate) async fn did_delete_row<F, O>(
-        &mut self,
-        row_rev: &RowRevision,
-        get_field_fn: F,
-    ) -> Option<Vec<GroupChangesetPB>>
-    where
-        F: FnOnce(String) -> O,
-        O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
-    {
-        let group_controller = self.group_controller.as_mut()?;
-        let field_id = group_controller.field_id().to_owned();
-        let field_rev = get_field_fn(field_id).await?;
-
-        match group_controller.did_delete_row(row_rev, &field_rev) {
-            Ok(changesets) => Some(changesets),
-            Err(e) => {
-                tracing::error!("Delete group data failed, {:?}", e);
-                None
-            }
-        }
-    }
-
-    pub(crate) async fn move_group_row<F, O>(
-        &mut self,
-        row_rev: &RowRevision,
-        row_changeset: &mut RowChangeset,
-        to_group_id: &str,
-        to_row_id: Option<String>,
-        get_field_fn: F,
-    ) -> Option<Vec<GroupChangesetPB>>
-    where
-        F: FnOnce(String) -> O,
-        O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
-    {
-        let group_controller = self.group_controller.as_mut()?;
-        let field_id = group_controller.field_id().to_owned();
-        let field_rev = get_field_fn(field_id).await?;
-        let move_row_context = MoveGroupRowContext {
-            row_rev,
-            row_changeset,
-            field_rev: field_rev.as_ref(),
-            to_group_id,
-            to_row_id,
-        };
-
-        match group_controller.move_group_row(move_row_context) {
-            Ok(changesets) => Some(changesets),
-            Err(e) => {
-                tracing::error!("Move group data failed, {:?}", e);
-                None
-            }
-        }
-    }
-
-    #[tracing::instrument(level = "trace", skip_all)]
-    pub(crate) async fn did_update_row<F, O>(
-        &mut self,
-        row_rev: &RowRevision,
-        get_field_fn: F,
-    ) -> Option<Vec<GroupChangesetPB>>
-    where
-        F: FnOnce(String) -> O,
-        O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
-    {
-        let group_controller = self.group_controller.as_mut()?;
-        let field_id = group_controller.field_id().to_owned();
-        let field_rev = get_field_fn(field_id).await?;
-
-        match group_controller.did_update_row(row_rev, &field_rev) {
-            Ok(changeset) => Some(changeset),
-            Err(e) => {
-                tracing::error!("Update group data failed, {:?}", e);
-                None
-            }
-        }
-    }
-
-    #[tracing::instrument(level = "trace", skip_all)]
-    pub(crate) async fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
-        match self.group_controller.as_mut() {
-            None => Ok(()),
-            Some(group_controller) => {
-                let _ = group_controller.move_group(from_group_id, to_group_id)?;
-                Ok(())
-            }
-        }
-    }
-
-    #[tracing::instrument(level = "trace", name = "group_did_update_field", skip(self, field_rev), err)]
-    pub(crate) async fn did_update_field(
-        &mut self,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<Option<GroupViewChangesetPB>> {
-        match self.group_controller.as_mut() {
-            None => Ok(None),
-            Some(group_controller) => group_controller.did_update_field(field_rev),
-        }
-    }
-
-    #[tracing::instrument(level = "trace", skip(self, field_rev), err)]
-    async fn make_group_controller(
-        &self,
-        field_type: &FieldType,
-        field_rev: &Arc<FieldRevision>,
-    ) -> FlowyResult<Option<Box<dyn GroupController>>> {
-        let mut group_controller: Option<Box<dyn GroupController>> = None;
-        match field_type {
-            FieldType::RichText => {
-                // let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
-            }
-            FieldType::Number => {
-                // let generator = GroupGenerator::<NumberGroupConfigurationPB>::from_configuration(configuration);
-            }
-            FieldType::DateTime => {
-                // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
-            }
-            FieldType::SingleSelect => {
-                let configuration = SelectOptionGroupConfiguration::new(
-                    self.view_id.clone(),
-                    field_rev.clone(),
-                    self.configuration_reader.clone(),
-                    self.configuration_writer.clone(),
-                )
-                .await?;
-                let controller = SingleSelectGroupController::new(field_rev, configuration).await?;
-                group_controller = Some(Box::new(controller));
-            }
-            FieldType::MultiSelect => {
-                let configuration = SelectOptionGroupConfiguration::new(
-                    self.view_id.clone(),
-                    field_rev.clone(),
-                    self.configuration_reader.clone(),
-                    self.configuration_writer.clone(),
-                )
-                .await?;
-                let controller = MultiSelectGroupController::new(field_rev, configuration).await?;
-                group_controller = Some(Box::new(controller));
-            }
-            FieldType::Checkbox => {
-                let configuration = CheckboxGroupConfiguration::new(
-                    self.view_id.clone(),
-                    field_rev.clone(),
-                    self.configuration_reader.clone(),
-                    self.configuration_writer.clone(),
-                )
-                .await?;
-                let controller = CheckboxGroupController::new(field_rev, configuration).await?;
-                group_controller = Some(Box::new(controller));
-            }
-            FieldType::URL => {
-                // let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
-            }
-        }
-        Ok(group_controller)
-    }
-}
-
-#[tracing::instrument(level = "trace", skip_all, err)]
-pub async fn make_group_controller<R, W>(
-    view_id: String,
-    field_revs: Vec<Arc<FieldRevision>>,
-    row_revs: Vec<Arc<RowRevision>>,
-    configuration_reader: R,
-    configuration_writer: W,
-) -> FlowyResult<Box<dyn GroupController>>
-where
-    R: GroupConfigurationReader,
-    W: GroupConfigurationWriter,
-{
-    let field_rev = find_group_field(&field_revs)?;
-    let field_type: FieldType = field_rev.ty.into();
-    let mut group_controller: Box<dyn GroupController>;
-    match field_type {
-        FieldType::RichText => {
-            // let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
-            panic!()
-        }
-        FieldType::Number => {
-            // let generator = GroupGenerator::<NumberGroupConfigurationPB>::from_configuration(configuration);
-            panic!()
-        }
-        FieldType::DateTime => {
-            // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
-            panic!()
-        }
-        FieldType::SingleSelect => {
-            let configuration = SelectOptionGroupConfiguration::new(
-                view_id,
-                field_rev.clone(),
-                configuration_reader,
-                configuration_writer,
-            )
-            .await?;
-            let controller = SingleSelectGroupController::new(&field_rev, configuration).await?;
-            group_controller = Box::new(controller);
-        }
-        FieldType::MultiSelect => {
-            let configuration = SelectOptionGroupConfiguration::new(
-                view_id,
-                field_rev.clone(),
-                configuration_reader,
-                configuration_writer,
-            )
-            .await?;
-            let controller = MultiSelectGroupController::new(&field_rev, configuration).await?;
-            group_controller = Box::new(controller);
-        }
-        FieldType::Checkbox => {
-            let configuration =
-                CheckboxGroupConfiguration::new(view_id, field_rev.clone(), configuration_reader, configuration_writer)
-                    .await?;
-            let controller = CheckboxGroupController::new(&field_rev, configuration).await?;
-            group_controller = Box::new(controller);
-        }
-        FieldType::URL => {
-            // let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
-            panic!()
-        }
-    }
-
-    let _ = group_controller.fill_groups(&row_revs, &field_rev)?;
-
-    Ok(group_controller)
-}
-
-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 default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
-    let field_id = field_rev.id.clone();
-    let field_type_rev = field_rev.ty;
-    let field_type: FieldType = field_rev.ty.into();
-    match field_type {
-        FieldType::RichText => {
-            GroupConfigurationRevision::new(field_id, field_type_rev, TextGroupConfigurationRevision::default())
-                .unwrap()
-        }
-        FieldType::Number => {
-            GroupConfigurationRevision::new(field_id, field_type_rev, NumberGroupConfigurationRevision::default())
-                .unwrap()
-        }
-        FieldType::DateTime => {
-            GroupConfigurationRevision::new(field_id, field_type_rev, DateGroupConfigurationRevision::default())
-                .unwrap()
-        }
-
-        FieldType::SingleSelect => GroupConfigurationRevision::new(
-            field_id,
-            field_type_rev,
-            SelectOptionGroupConfigurationRevision::default(),
-        )
-        .unwrap(),
-        FieldType::MultiSelect => GroupConfigurationRevision::new(
-            field_id,
-            field_type_rev,
-            SelectOptionGroupConfigurationRevision::default(),
-        )
-        .unwrap(),
-        FieldType::Checkbox => {
-            GroupConfigurationRevision::new(field_id, field_type_rev, CheckboxGroupConfigurationRevision::default())
-                .unwrap()
-        }
-        FieldType::URL => {
-            GroupConfigurationRevision::new(field_id, field_type_rev, UrlGroupConfigurationRevision::default()).unwrap()
-        }
-    }
-}

+ 113 - 0
frontend/rust-lib/flowy-grid/src/services/group/group_util.rs

@@ -0,0 +1,113 @@
+use crate::entities::FieldType;
+use crate::services::group::configuration::GroupConfigurationReader;
+use crate::services::group::controller::GroupController;
+use crate::services::group::{
+    CheckboxGroupContext, CheckboxGroupController, DefaultGroupController, GroupConfigurationWriter,
+    MultiSelectGroupController, SelectOptionGroupContext, SingleSelectGroupController,
+};
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::{
+    CheckboxGroupConfigurationRevision, DateGroupConfigurationRevision, FieldRevision, GroupConfigurationRevision,
+    NumberGroupConfigurationRevision, RowRevision, SelectOptionGroupConfigurationRevision,
+    TextGroupConfigurationRevision, UrlGroupConfigurationRevision,
+};
+use std::sync::Arc;
+
+#[tracing::instrument(level = "trace", skip_all, err)]
+pub async fn make_group_controller<R, W>(
+    view_id: String,
+    field_revs: Vec<Arc<FieldRevision>>,
+    row_revs: Vec<Arc<RowRevision>>,
+    configuration_reader: R,
+    configuration_writer: W,
+) -> FlowyResult<Box<dyn GroupController>>
+where
+    R: GroupConfigurationReader,
+    W: GroupConfigurationWriter,
+{
+    let field_rev = find_group_field(&field_revs).unwrap();
+    let field_type: FieldType = field_rev.ty.into();
+    let mut group_controller: Box<dyn GroupController>;
+    let configuration_reader = Arc::new(configuration_reader);
+    let configuration_writer = Arc::new(configuration_writer);
+    match field_type {
+        FieldType::SingleSelect => {
+            let configuration =
+                SelectOptionGroupContext::new(view_id, field_rev.clone(), configuration_reader, configuration_writer)
+                    .await?;
+            let controller = SingleSelectGroupController::new(&field_rev, configuration).await?;
+            group_controller = Box::new(controller);
+        }
+        FieldType::MultiSelect => {
+            let configuration =
+                SelectOptionGroupContext::new(view_id, field_rev.clone(), configuration_reader, configuration_writer)
+                    .await?;
+            let controller = MultiSelectGroupController::new(&field_rev, configuration).await?;
+            group_controller = Box::new(controller);
+        }
+        FieldType::Checkbox => {
+            let configuration =
+                CheckboxGroupContext::new(view_id, field_rev.clone(), configuration_reader, configuration_writer)
+                    .await?;
+            let controller = CheckboxGroupController::new(&field_rev, configuration).await?;
+            group_controller = Box::new(controller);
+        }
+        _ => {
+            group_controller = Box::new(DefaultGroupController::new(&field_rev));
+        }
+    }
+
+    let _ = group_controller.fill_groups(&row_revs, &field_rev)?;
+    Ok(group_controller)
+}
+
+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 default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
+    let field_id = field_rev.id.clone();
+    let field_type_rev = field_rev.ty;
+    let field_type: FieldType = field_rev.ty.into();
+    match field_type {
+        FieldType::RichText => {
+            GroupConfigurationRevision::new(field_id, field_type_rev, TextGroupConfigurationRevision::default())
+                .unwrap()
+        }
+        FieldType::Number => {
+            GroupConfigurationRevision::new(field_id, field_type_rev, NumberGroupConfigurationRevision::default())
+                .unwrap()
+        }
+        FieldType::DateTime => {
+            GroupConfigurationRevision::new(field_id, field_type_rev, DateGroupConfigurationRevision::default())
+                .unwrap()
+        }
+
+        FieldType::SingleSelect => GroupConfigurationRevision::new(
+            field_id,
+            field_type_rev,
+            SelectOptionGroupConfigurationRevision::default(),
+        )
+        .unwrap(),
+        FieldType::MultiSelect => GroupConfigurationRevision::new(
+            field_id,
+            field_type_rev,
+            SelectOptionGroupConfigurationRevision::default(),
+        )
+        .unwrap(),
+        FieldType::Checkbox => {
+            GroupConfigurationRevision::new(field_id, field_type_rev, CheckboxGroupConfigurationRevision::default())
+                .unwrap()
+        }
+        FieldType::URL => {
+            GroupConfigurationRevision::new(field_id, field_type_rev, UrlGroupConfigurationRevision::default()).unwrap()
+        }
+    }
+}

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/group/mod.rs

@@ -3,10 +3,10 @@ mod configuration;
 mod controller;
 mod controller_impls;
 mod entities;
-mod group_service;
+mod group_util;
 
 pub(crate) use configuration::*;
 pub(crate) use controller::*;
 pub(crate) use controller_impls::*;
 pub(crate) use entities::*;
-pub(crate) use group_service::*;
+pub(crate) use group_util::*;

+ 8 - 8
frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs

@@ -391,11 +391,11 @@ async fn group_update_field_test() {
     test.run_scripts(scripts).await;
 }
 
-#[tokio::test]
-async fn group_multi_select_field_test() {
-    let mut test = GridGroupTest::new().await;
-    let multi_select_field = test.get_multi_select_field().await;
-
-    let scripts = vec![];
-    test.run_scripts(scripts).await;
-}
+// #[tokio::test]
+// async fn group_multi_select_field_test() {
+//     let mut test = GridGroupTest::new().await;
+//     let multi_select_field = test.get_multi_select_field().await;
+//
+//     let scripts = vec![];
+//     test.run_scripts(scripts).await;
+// }

+ 16 - 22
shared-lib/flowy-grid-data-model/src/revision/group_rev.rs

@@ -4,9 +4,8 @@ use serde_json::Error;
 use serde_repr::*;
 
 pub trait GroupConfigurationContentSerde: Sized + Send + Sync {
-    fn from_configuration_content(s: &str) -> Result<Self, serde_json::Error>;
-
-    fn to_configuration_content(&self) -> Result<String, serde_json::Error>;
+    fn from_json(s: &str) -> Result<Self, serde_json::Error>;
+    fn to_json(&self) -> Result<String, serde_json::Error>;
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -15,6 +14,7 @@ pub struct GroupConfigurationRevision {
     pub field_id: String,
     pub field_type_rev: FieldTypeRevision,
     pub groups: Vec<GroupRevision>,
+    // This content is serde in Json format
     pub content: String,
 }
 
@@ -23,7 +23,7 @@ impl GroupConfigurationRevision {
     where
         T: GroupConfigurationContentSerde,
     {
-        let content = content.to_configuration_content()?;
+        let content = content.to_json()?;
         Ok(Self {
             id: gen_grid_group_id(),
             field_id,
@@ -40,10 +40,10 @@ pub struct TextGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for TextGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
@@ -54,10 +54,10 @@ pub struct NumberGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for NumberGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
@@ -68,10 +68,10 @@ pub struct UrlGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for UrlGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
@@ -82,11 +82,11 @@ pub struct CheckboxGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for CheckboxGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
 
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
@@ -97,11 +97,11 @@ pub struct SelectOptionGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for SelectOptionGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
 
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
@@ -113,22 +113,17 @@ pub struct GroupRevision {
     #[serde(default)]
     pub name: String,
 
-    #[serde(skip, default = "IS_DEFAULT_GROUP")]
-    pub is_default: bool,
-
     #[serde(default = "GROUP_REV_VISIBILITY")]
     pub visible: bool,
 }
 
 const GROUP_REV_VISIBILITY: fn() -> bool = || true;
-const IS_DEFAULT_GROUP: fn() -> bool = || false;
 
 impl GroupRevision {
     pub fn new(id: String, group_name: String) -> Self {
         Self {
             id,
             name: group_name,
-            is_default: false,
             visible: true,
         }
     }
@@ -137,7 +132,6 @@ impl GroupRevision {
         Self {
             id,
             name: group_name,
-            is_default: true,
             visible: true,
         }
     }
@@ -150,10 +144,10 @@ pub struct DateGroupConfigurationRevision {
 }
 
 impl GroupConfigurationContentSerde for DateGroupConfigurationRevision {
-    fn from_configuration_content(s: &str) -> Result<Self, Error> {
+    fn from_json(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
-    fn to_configuration_content(&self) -> Result<String, Error> {
+    fn to_json(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }