From b557f89829413c75199cb7fbecbbfb467e3bd306 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:18:40 +0800 Subject: [PATCH] refactor: group controller traits (#4880) * chore: remove async trait * chore: require From for trait TypeOption * refactor: simplify group controller by removing GroupController trait * chore: rename GroupControllerOperation trait to GroupController * chore: add some docs * chore: remove plural struct and use Vec directly * chore: unnecessary mut * chore: use same name to indicate passthrough * chore: remove unused trait * chore: rename group context to group controller context * chore: remove group name * chore: move type option interceptor to GroupCustomize and split off delegates for group context and group controller * chore: adapt tests to changes * chore: adapt flutter frontend to changes * chore: code cleanup * chore: fix clippy and adapt tauri frontend to changes * chore: group controller code clean up * chore: no need to pass cell data when moving row * chore: rename some functions and variables * chore: remove content filter --- .../widgets/mobile_hidden_groups_column.dart | 2 +- .../board/application/board_bloc.dart | 72 ++++++- .../board/application/group_controller.dart | 18 +- .../widgets/board_hidden_groups.dart | 4 +- .../group_by_multi_select_field_test.dart | 12 -- .../application/database/group/group_types.ts | 2 - .../tests/database/local_test/group_test.rs | 14 -- .../src/entities/group_entities/group.rs | 4 - .../group_entities/group_changeset.rs | 22 +-- .../flowy-database2/src/event_handler.rs | 2 +- .../src/services/database/database_editor.rs | 8 +- .../src/services/database_view/view_editor.rs | 43 ++--- .../src/services/database_view/view_group.rs | 82 +++----- .../src/services/database_view/views.rs | 27 --- .../field/type_options/type_option.rs | 2 +- .../field/type_options/type_option_cell.rs | 25 +-- .../src/services/group/action.rs | 50 +++-- .../src/services/group/configuration.rs | 144 ++++---------- .../src/services/group/controller.rs | 131 ++++++------- .../controller_impls/checkbox_controller.rs | 57 ++---- .../group/controller_impls/date_controller.rs | 177 ++++-------------- .../controller_impls/default_controller.rs | 26 +-- .../multi_select_controller.rs | 118 +++++------- .../single_select_controller.rs | 122 +++++------- .../select_option_controller/util.rs | 12 +- .../group/controller_impls/url_controller.rs | 69 ++----- .../src/services/group/entities.rs | 37 +--- .../src/services/group/group_builder.rs | 130 ++++--------- .../flowy-database2/src/services/group/mod.rs | 1 + .../database/group_test/date_group_test.rs | 21 +-- .../tests/database/group_test/script.rs | 8 +- .../tests/database/group_test/test.rs | 10 +- 32 files changed, 490 insertions(+), 962 deletions(-) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/widgets/mobile_hidden_groups_column.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/widgets/mobile_hidden_groups_column.dart index 078da42b9d..549a7f4a8a 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/widgets/mobile_hidden_groups_column.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/widgets/mobile_hidden_groups_column.dart @@ -199,7 +199,7 @@ class MobileHiddenGroup extends StatelessWidget { children: [ Expanded( child: Text( - group.groupName, + context.read().generateGroupNameFromGroup(group), style: Theme.of(context).textTheme.bodyMedium, maxLines: 2, overflow: TextOverflow.ellipsis, diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart index 8ea1d004fa..70402f38fd 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart @@ -11,9 +11,11 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_board/appflowy_board.dart'; import 'package:appflowy_result/appflowy_result.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:intl/intl.dart'; import 'package:protobuf/protobuf.dart' hide FieldInfo; import '../../application/database_controller.dart'; @@ -397,7 +399,7 @@ class BoardBloc extends Bloc { boardController.getGroupController(group.groupId); if (columnController != null) { // remove the group or update its name - columnController.updateGroupName(group.groupName); + columnController.updateGroupName(generateGroupNameFromGroup(group)); if (!group.isVisible) { boardController.removeGroup(group.groupId); } @@ -491,7 +493,7 @@ class BoardBloc extends Bloc { AppFlowyGroupData _initializeGroupData(GroupPB group) { return AppFlowyGroupData( id: group.groupId, - name: group.groupName, + name: generateGroupNameFromGroup(group), items: _buildGroupItems(group), customData: GroupData( group: group, @@ -499,6 +501,72 @@ class BoardBloc extends Bloc { ), ); } + + String generateGroupNameFromGroup(GroupPB group) { + final field = fieldController.getField(group.fieldId); + if (field == null) { + return ""; + } + + // if the group is the default group, then + if (group.isDefault) { + return "No ${field.name}"; + } + + switch (field.fieldType) { + case FieldType.SingleSelect: + final options = + SingleSelectTypeOptionPB.fromBuffer(field.field.typeOptionData) + .options; + final option = + options.firstWhereOrNull((option) => option.id == group.groupId); + return option == null ? "" : option.name; + case FieldType.MultiSelect: + final options = + MultiSelectTypeOptionPB.fromBuffer(field.field.typeOptionData) + .options; + final option = + options.firstWhereOrNull((option) => option.id == group.groupId); + return option == null ? "" : option.name; + case FieldType.Checkbox: + return group.groupId; + case FieldType.URL: + return group.groupId; + case FieldType.DateTime: + // Assume DateCondition::Relative as there isn't an option for this + // right now. + final dateFormat = DateFormat("y/MM/dd"); + try { + final targetDateTime = dateFormat.parseLoose(group.groupId); + final targetDateTimeDay = DateTime( + targetDateTime.year, + targetDateTime.month, + targetDateTime.day, + ); + final now = DateTime.now(); + final nowDay = DateTime( + now.year, + now.month, + now.day, + ); + final diff = targetDateTimeDay.difference(nowDay).inDays; + return switch (diff) { + 0 => "Today", + -1 => "Yesterday", + 1 => "Tomorrow", + -7 => "Last 7 days", + 2 => "Next 7 days", + -30 => "Last 30 days", + 8 => "Next 30 days", + _ => DateFormat("MMM y").format(targetDateTimeDay) + }; + } on FormatException { + return ""; + } + default: + return ""; + } + } } @freezed diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/application/group_controller.dart b/frontend/appflowy_flutter/lib/plugins/database/board/application/group_controller.dart index 5f1b8c9609..7e0700e263 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/application/group_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/application/group_controller.dart @@ -31,23 +31,11 @@ class GroupController { final GroupControllerDelegate delegate; final void Function(GroupPB group) onGroupChanged; - RowMetaPB? rowAtIndex(int index) { - if (index < group.rows.length) { - return group.rows[index]; - } else { - return null; - } - } + RowMetaPB? rowAtIndex(int index) => group.rows.elementAtOrNull(index); - RowMetaPB? firstRow() { - if (group.rows.isEmpty) return null; - return group.rows.first; - } + RowMetaPB? firstRow() => group.rows.firstOrNull; - RowMetaPB? lastRow() { - if (group.rows.isEmpty) return null; - return group.rows.last; - } + RowMetaPB? lastRow() => group.rows.lastOrNull; void startListening() { _listener.start( diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart index 82b11a0fd1..0469378c8b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart @@ -269,7 +269,7 @@ class HiddenGroupButtonContent extends StatelessWidget { ), const HSpace(4), FlowyText.medium( - group.groupName, + bloc.generateGroupNameFromGroup(group), overflow: TextOverflow.ellipsis, ), const HSpace(6), @@ -369,7 +369,7 @@ class HiddenGroupPopupItemList extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), child: FlowyText.medium( - group.groupName, + context.read().generateGroupNameFromGroup(group), fontSize: 10, color: Theme.of(context).hintColor, overflow: TextOverflow.ellipsis, diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart index dd07369d9d..efe89e5bd5 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart @@ -49,12 +49,6 @@ void main() { boardBloc.groupControllers.values.length == 1, "Expected 1, but receive ${boardBloc.groupControllers.values.length}", ); - final expectedGroupName = "No ${multiSelectField.name}"; - assert( - boardBloc.groupControllers.values.first.group.groupName == - expectedGroupName, - "Expected $expectedGroupName, but receive ${boardBloc.groupControllers.values.first.group.groupName}", - ); }); test('group by multi select with no options test', () async { @@ -105,11 +99,5 @@ void main() { boardBloc.groupControllers.values.length == 3, "Expected 3, but receive ${boardBloc.groupControllers.values.length}", ); - - final groups = - boardBloc.groupControllers.values.map((e) => e.group).toList(); - assert(groups[0].groupName == "No ${multiSelectField.name}"); - assert(groups[1].groupName == "B"); - assert(groups[2].groupName == "A"); }); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts index ad0c8af542..b75ecc0bd4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts @@ -8,7 +8,6 @@ export interface GroupSetting { export interface Group { id: string; - name: string; isDefault: boolean; isVisible: boolean; fieldId: string; @@ -18,7 +17,6 @@ export interface Group { export function pbToGroup(pb: GroupPB): Group { return { id: pb.group_id, - name: pb.group_name, isDefault: pb.is_default, isVisible: pb.is_visible, fieldId: pb.field_id, diff --git a/frontend/rust-lib/event-integration/tests/database/local_test/group_test.rs b/frontend/rust-lib/event-integration/tests/database/local_test/group_test.rs index e9ed09813a..556624e7ff 100644 --- a/frontend/rust-lib/event-integration/tests/database/local_test/group_test.rs +++ b/frontend/rust-lib/event-integration/tests/database/local_test/group_test.rs @@ -89,7 +89,6 @@ async fn rename_group_event_test() { .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) .await; - // Empty to group id let groups = test.get_groups(&board_view.id).await; let error = test .update_group( @@ -101,9 +100,6 @@ async fn rename_group_event_test() { ) .await; assert!(error.is_none()); - - let groups = test.get_groups(&board_view.id).await; - assert_eq!(groups[1].group_name, "new name".to_owned()); } #[tokio::test] @@ -144,9 +140,6 @@ async fn update_group_name_test() { let groups = test.get_groups(&board_view.id).await; assert_eq!(groups.len(), 4); - assert_eq!(groups[1].group_name, "To Do"); - assert_eq!(groups[2].group_name, "Doing"); - assert_eq!(groups[3].group_name, "Done"); test .update_group( @@ -160,8 +153,6 @@ async fn update_group_name_test() { let groups = test.get_groups(&board_view.id).await; assert_eq!(groups.len(), 4); - assert_eq!(groups[1].group_name, "To Do?"); - assert_eq!(groups[2].group_name, "Doing"); } #[tokio::test] @@ -174,14 +165,9 @@ async fn delete_group_test() { let groups = test.get_groups(&board_view.id).await; assert_eq!(groups.len(), 4); - assert_eq!(groups[1].group_name, "To Do"); - assert_eq!(groups[2].group_name, "Doing"); - assert_eq!(groups[3].group_name, "Done"); test.delete_group(&board_view.id, &groups[1].group_id).await; let groups = test.get_groups(&board_view.id).await; assert_eq!(groups.len(), 3); - assert_eq!(groups[1].group_name, "Doing"); - assert_eq!(groups[2].group_name, "Done"); } diff --git a/frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs b/frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs index 05cc0c2723..9f40685702 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs @@ -76,9 +76,6 @@ pub struct GroupPB { #[pb(index = 2)] pub group_id: String, - #[pb(index = 3)] - pub group_name: String, - #[pb(index = 4)] pub rows: Vec, @@ -94,7 +91,6 @@ impl std::convert::From for GroupPB { Self { field_id: group_data.field_id, group_id: group_data.id, - group_name: group_data.name, rows: group_data.rows.into_iter().map(RowMetaPB::from).collect(), is_default: group_data.is_default, is_visible: group_data.is_visible, diff --git a/frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs b/frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs index 59bab13169..f002e93bd2 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs @@ -11,16 +11,13 @@ pub struct GroupRowsNotificationPB { #[pb(index = 1)] pub group_id: String, - #[pb(index = 2, one_of)] - pub group_name: Option, - - #[pb(index = 3)] + #[pb(index = 2)] pub inserted_rows: Vec, - #[pb(index = 4)] + #[pb(index = 3)] pub deleted_rows: Vec, - #[pb(index = 5)] + #[pb(index = 4)] pub updated_rows: Vec, } @@ -43,10 +40,7 @@ impl std::fmt::Display for GroupRowsNotificationPB { impl GroupRowsNotificationPB { pub fn is_empty(&self) -> bool { - self.group_name.is_none() - && self.inserted_rows.is_empty() - && self.deleted_rows.is_empty() - && self.updated_rows.is_empty() + self.inserted_rows.is_empty() && self.deleted_rows.is_empty() && self.updated_rows.is_empty() } pub fn new(group_id: String) -> Self { @@ -56,14 +50,6 @@ impl GroupRowsNotificationPB { } } - pub fn name(group_id: String, name: &str) -> Self { - Self { - group_id, - group_name: Some(name.to_owned()), - ..Default::default() - } - } - pub fn insert(group_id: String, inserted_rows: Vec) -> Self { Self { group_id, diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index c3a714a0cf..1e40e61354 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -668,7 +668,7 @@ pub(crate) async fn update_group_handler( let (tx, rx) = oneshot::channel(); af_spawn(async move { let result = database_editor - .update_group(&view_id, vec![group_changeset].into()) + .update_group(&view_id, vec![group_changeset]) .await; let _ = tx.send(result); }); diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 7f5878ab53..f7d3482480 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -35,7 +35,7 @@ use crate::services::field_settings::{ default_field_settings_by_layout_map, FieldSettings, FieldSettingsChangesetParams, }; use crate::services::filter::{Filter, FilterChangeset}; -use crate::services::group::{default_group_setting, GroupChangesets, GroupSetting, RowChangeset}; +use crate::services::group::{default_group_setting, GroupChangeset, GroupSetting, RowChangeset}; use crate::services::share::csv::{CSVExport, CSVFormat}; use crate::services::sort::Sort; use crate::utils::cache::AnyTypeCache; @@ -210,7 +210,11 @@ impl DatabaseEditor { Ok(self.database.lock().delete_view(view_id)) } - pub async fn update_group(&self, view_id: &str, changesets: GroupChangesets) -> FlowyResult<()> { + pub async fn update_group( + &self, + view_id: &str, + changesets: Vec, + ) -> FlowyResult<()> { let view_editor = self.database_views.get_view_editor(view_id).await?; view_editor.v_update_group(changesets).await?; Ok(()) diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs index 2208bdeb23..398d9e8dff 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs @@ -37,7 +37,7 @@ use crate::services::database_view::{ }; use crate::services::field_settings::FieldSettings; use crate::services::filter::{Filter, FilterChangeset, FilterController}; -use crate::services::group::{GroupChangesets, GroupController, MoveGroupRowContext, RowChangeset}; +use crate::services::group::{GroupChangeset, GroupController, MoveGroupRowContext, RowChangeset}; use crate::services::setting::CalendarLayoutSetting; use crate::services::sort::{Sort, SortChangeset, SortController}; @@ -139,12 +139,13 @@ impl DatabaseViewEditor { // fill in cells according to group_id if supplied if let Some(group_id) = params.group_id { - let _ = self - .mut_group_controller(|group_controller, field| { - group_controller.will_create_row(&mut cells, &field, &group_id); - Ok(()) - }) - .await; + if let Some(controller) = self.group_controller.read().await.as_ref() { + let field = self + .delegate + .get_field(controller.field_id()) + .ok_or_else(|| FlowyError::internal().with_context("Failed to get grouping field"))?; + controller.will_create_row(&mut cells, &field, &group_id); + } } // fill in cells according to active filters @@ -460,19 +461,18 @@ impl DatabaseViewEditor { Ok(changes) } - pub async fn v_update_group(&self, changeset: GroupChangesets) -> FlowyResult<()> { + pub async fn v_update_group(&self, changeset: Vec) -> FlowyResult<()> { let mut type_option_data = TypeOptionData::new(); - let (old_field, updated_groups) = if let Some(controller) = - self.group_controller.write().await.as_mut() - { - let old_field = self.delegate.get_field(controller.field_id()); - let (updated_groups, new_type_option) = controller.apply_group_changeset(&changeset).await?; - type_option_data.extend(new_type_option); + let (old_field, updated_groups) = + if let Some(controller) = self.group_controller.write().await.as_mut() { + let old_field = self.delegate.get_field(controller.field_id()); + let (updated_groups, new_type_option) = controller.apply_group_changeset(&changeset)?; + type_option_data.extend(new_type_option); - (old_field, updated_groups) - } else { - (None, vec![]) - }; + (old_field, updated_groups) + } else { + (None, vec![]) + }; if let Some(old_field) = old_field { if !type_option_data.is_empty() { @@ -800,13 +800,6 @@ impl DatabaseViewEditor { .did_update_field_type_option(&field) .await; - self - .mut_group_controller(|group_controller, _| { - group_controller.did_update_field_type_option(&field); - Ok(()) - }) - .await; - if old_field.field_type != field.field_type { let changeset = FilterChangeset::DeleteAllWithFieldId { field_id: field.id.clone(), diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs index 0dba71a570..a2cbdaca82 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs @@ -1,8 +1,7 @@ use std::sync::Arc; -use async_trait::async_trait; use collab_database::fields::Field; -use collab_database::rows::{Cell, RowId}; +use collab_database::rows::RowId; use flowy_error::FlowyResult; use lib_infra::future::{to_fut, Fut}; @@ -11,8 +10,8 @@ use crate::entities::FieldType; use crate::services::database_view::DatabaseViewOperation; use crate::services::field::RowSingleCellData; use crate::services::group::{ - find_new_grouping_field, make_group_controller, GroupController, GroupSetting, - GroupSettingReader, GroupSettingWriter, GroupTypeOptionCellOperation, + find_suitable_grouping_field, make_group_controller, GroupContextDelegate, GroupController, + GroupControllerDelegate, GroupSetting, }; pub async fn new_group_controller_with_field( @@ -20,19 +19,9 @@ pub async fn new_group_controller_with_field( delegate: Arc, grouping_field: Field, ) -> FlowyResult> { - let setting_reader = GroupSettingReaderImpl(delegate.clone()); + let configuration_delegate = GroupControllerDelegateImpl(delegate.clone()); let rows = delegate.get_rows(&view_id).await; - let setting_writer = GroupSettingWriterImpl(delegate.clone()); - let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone()); - make_group_controller( - view_id, - grouping_field, - rows, - setting_reader, - setting_writer, - type_option_writer, - ) - .await + make_group_controller(view_id, grouping_field, rows, configuration_delegate).await } pub async fn new_group_controller( @@ -40,10 +29,10 @@ pub async fn new_group_controller( delegate: Arc, ) -> FlowyResult>> { let fields = delegate.get_fields(&view_id, None).await; - let setting_reader = GroupSettingReaderImpl(delegate.clone()); + let controller_delegate = GroupControllerDelegateImpl(delegate.clone()); // Read the grouping field or find a new grouping field - let mut grouping_field = setting_reader + let mut grouping_field = controller_delegate .get_group_setting(&view_id) .await .and_then(|setting| { @@ -56,32 +45,22 @@ pub async fn new_group_controller( let layout = delegate.get_layout_for_view(&view_id); // If the view is a board and the grouping field is empty, we need to find a new grouping field if layout.is_board() && grouping_field.is_none() { - grouping_field = find_new_grouping_field(&fields, &layout); + grouping_field = find_suitable_grouping_field(&fields); } if let Some(grouping_field) = grouping_field { let rows = delegate.get_rows(&view_id).await; - let setting_writer = GroupSettingWriterImpl(delegate.clone()); - let type_option_writer = GroupTypeOptionCellWriterImpl(delegate.clone()); Ok(Some( - make_group_controller( - view_id, - grouping_field, - rows, - setting_reader, - setting_writer, - type_option_writer, - ) - .await?, + make_group_controller(view_id, grouping_field, rows, controller_delegate).await?, )) } else { Ok(None) } } -pub(crate) struct GroupSettingReaderImpl(pub Arc); +pub(crate) struct GroupControllerDelegateImpl(pub Arc); -impl GroupSettingReader for GroupSettingReaderImpl { +impl GroupContextDelegate for GroupControllerDelegateImpl { fn get_group_setting(&self, view_id: &str) -> Fut>> { let mut settings = self.0.get_group_setting(view_id); to_fut(async move { @@ -99,6 +78,17 @@ impl GroupSettingReader for GroupSettingReaderImpl { let delegate = self.0.clone(); to_fut(async move { get_cells_for_field(delegate, &view_id, &field_id).await }) } + + fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut> { + self.0.insert_group_setting(view_id, group_setting); + to_fut(async move { Ok(()) }) + } +} + +impl GroupControllerDelegate for GroupControllerDelegateImpl { + fn get_field(&self, field_id: &str) -> Option { + self.0.get_field(field_id) + } } pub(crate) async fn get_cell_for_row( @@ -153,31 +143,3 @@ pub(crate) async fn get_cells_for_field( vec![] } - -struct GroupSettingWriterImpl(Arc); -impl GroupSettingWriter for GroupSettingWriterImpl { - fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut> { - self.0.insert_group_setting(view_id, group_setting); - to_fut(async move { Ok(()) }) - } -} - -struct GroupTypeOptionCellWriterImpl(Arc); - -#[async_trait] -impl GroupTypeOptionCellOperation for GroupTypeOptionCellWriterImpl { - async fn get_cell(&self, _row_id: &RowId, _field_id: &str) -> FlowyResult> { - todo!() - } - - #[tracing::instrument(level = "trace", skip_all, err)] - async fn update_cell( - &self, - _view_id: &str, - _row_id: &RowId, - _field_id: &str, - _cell: Cell, - ) -> FlowyResult<()> { - todo!() - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs index 005cb48443..ed949d7287 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/views.rs @@ -2,17 +2,14 @@ use std::collections::HashMap; use std::sync::Arc; use collab_database::database::MutexDatabase; -use collab_database::rows::{RowDetail, RowId}; use nanoid::nanoid; use tokio::sync::{broadcast, RwLock}; use flowy_error::{FlowyError, FlowyResult}; -use lib_infra::future::Fut; use crate::services::cell::CellCache; use crate::services::database::DatabaseRowEvent; use crate::services::database_view::{DatabaseViewEditor, DatabaseViewOperation}; -use crate::services::group::RowChangeset; pub type RowEventSender = broadcast::Sender; pub type RowEventReceiver = broadcast::Receiver; @@ -59,30 +56,6 @@ impl DatabaseViews { .collect() } - /// It may generate a RowChangeset when the Row was moved from one group to another. - /// The return value, [RowChangeset], contains the changes made by the groups. - /// - pub async fn move_group_row( - &self, - view_id: &str, - row_detail: Arc, - to_group_id: String, - to_row_id: Option, - recv_row_changeset: impl FnOnce(RowChangeset) -> Fut<()>, - ) -> FlowyResult<()> { - let view_editor = self.get_view_editor(view_id).await?; - let mut row_changeset = RowChangeset::new(row_detail.row.id.clone()); - view_editor - .v_move_group_row(&row_detail, &mut row_changeset, &to_group_id, to_row_id) - .await; - - if !row_changeset.is_empty() { - recv_row_changeset(row_changeset).await; - } - - Ok(()) - } - pub async fn get_view_editor(&self, view_id: &str) -> FlowyResult> { debug_assert!(!view_id.is_empty()); if let Some(editor) = self.editor_by_view_id.read().await.get(view_id) { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs index 281ed3e684..0cb3d1ca65 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs @@ -22,7 +22,7 @@ use crate::services::field::{ use crate::services::filter::{ParseFilterData, PreFillCellsWithFilter}; use crate::services::sort::SortCondition; -pub trait TypeOption { +pub trait TypeOption: From + Into { /// `CellData` represents the decoded model for the current type option. Each of them must /// implement the From<&Cell> trait. If the `Cell` cannot be decoded into this type, the default /// value will be returned. diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs index 476aa3fd6c..0c2b8a73da 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs @@ -138,7 +138,7 @@ where cell: &Cell, decoded_field_type: &FieldType, field: &Field, - ) -> FlowyResult<::CellData> { + ) -> FlowyResult { let key = CellDataCacheKey::new(field, *decoded_field_type, cell); if let Some(cell_data_cache) = self.cell_data_cache.as_ref() { let read_guard = cell_data_cache.read(); @@ -168,12 +168,7 @@ where Ok(cell_data) } - fn set_decoded_cell_data( - &self, - cell: &Cell, - cell_data: ::CellData, - field: &Field, - ) { + fn set_decoded_cell_data(&self, cell: &Cell, cell_data: T::CellData, field: &Field) { if let Some(cell_data_cache) = self.cell_data_cache.as_ref() { let field_type = FieldType::from(field.field_type); let key = CellDataCacheKey::new(field, field_type, cell); @@ -196,16 +191,6 @@ impl std::ops::Deref for TypeOptionCellDataHandlerImpl { } } -impl TypeOption for TypeOptionCellDataHandlerImpl -where - T: TypeOption + Send + Sync, -{ - type CellData = T::CellData; - type CellChangeset = T::CellChangeset; - type CellProtobufType = T::CellProtobufType; - type CellFilter = T::CellFilter; -} - impl TypeOptionCellDataHandler for TypeOptionCellDataHandlerImpl where T: TypeOption @@ -227,7 +212,7 @@ where ) -> FlowyResult { let cell_data = self .get_cell_data(cell, decoded_field_type, field_rev)? - .unbox_or_default::<::CellData>(); + .unbox_or_default::(); CellProtobufBlob::from(self.protobuf_encode(cell_data)) } @@ -238,7 +223,7 @@ where old_cell: Option, field: &Field, ) -> FlowyResult { - let changeset = cell_changeset.unbox_or_error::<::CellChangeset>()?; + let changeset = cell_changeset.unbox_or_error::()?; let (cell, cell_data) = self.apply_changeset(changeset, old_cell)?; self.set_decoded_cell_data(&cell, cell_data, field); Ok(cell) @@ -306,7 +291,7 @@ where fn handle_cell_filter(&self, field: &Field, cell: &Cell, filter: &BoxAny) -> bool { let perform_filter = || { let field_type = FieldType::from(field.field_type); - let cell_filter = filter.downcast_ref::<::CellFilter>()?; + let cell_filter = filter.downcast_ref::()?; let cell_data = self.get_decoded_cell_data(cell, &field_type, field).ok()?; Some(self.apply_filter(cell_filter, &cell_data)) }; diff --git a/frontend/rust-lib/flowy-database2/src/services/group/action.rs b/frontend/rust-lib/flowy-database2/src/services/group/action.rs index 11bd169591..291624747b 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/action.rs @@ -1,16 +1,15 @@ -use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; -use collab_database::rows::{Cell, Row, RowDetail, RowId}; +use collab_database::rows::{Cell, Cells, Row, RowDetail, RowId}; use flowy_error::FlowyResult; use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; use crate::services::field::TypeOption; -use crate::services::group::{GroupChangesets, GroupData, MoveGroupRowContext}; +use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext}; -/// Using polymorphism to provides the customs action for different group controller. -/// -/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior. +/// [GroupCustomize] is implemented by parameterized `BaseGroupController`s to provide different +/// behaviors. This allows the BaseGroupController to call these actions indescriminantly using +/// polymorphism. /// pub trait GroupCustomize: Send + Sync { type GroupTypeOption: TypeOption; @@ -57,11 +56,7 @@ pub trait GroupCustomize: Send + Sync { ) -> (Option, Vec); /// Move row from one group to another - fn move_row( - &mut self, - cell_data: &::CellProtobufType, - context: MoveGroupRowContext, - ) -> Vec; + fn move_row(&mut self, context: MoveGroupRowContext) -> Vec; /// Returns None if there is no need to delete the group when corresponding row get removed fn delete_group_when_move_row( @@ -72,19 +67,35 @@ pub trait GroupCustomize: Send + Sync { None } - fn generate_new_group( + fn create_group( &mut self, _name: String, ) -> FlowyResult<(Option, Option)> { Ok((None, None)) } - fn delete_group_custom(&mut self, group_id: &str) -> FlowyResult>; + fn delete_group(&mut self, group_id: &str) -> FlowyResult>; + + fn update_type_option_when_update_group( + &mut self, + _changeset: &GroupChangeset, + _type_option: &mut Self::GroupTypeOption, + ) { + } + + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str); } -/// Defines the shared actions any group controller can perform. -#[async_trait] -pub trait GroupControllerOperation: Send + Sync { +/// The `GroupController` trait defines the behavior of the group controller when performing any +/// group-related tasks, such as managing rows within a group, transferring rows between groups, +/// manipulating groups themselves, and even pre-filling a row's cells before it is created. +/// +/// Depending on the type of the field that is being grouped, a parameterized `BaseGroupController` +/// or a `DefaultGroupController` may be the actual object that provides the functionality of +/// this trait. For example, a `Single-Select` group controller will be a `BaseGroupController`, +/// while a `URL` group controller will be a `DefaultGroupController`. +/// +pub trait GroupController: Send + Sync { /// Returns the id of field that is being used to group the rows fn field_id(&self) -> &str; @@ -175,10 +186,13 @@ pub trait GroupControllerOperation: Send + Sync { /// in the field type option data. /// /// * `changesets`: list of changesets to be made to one or more groups - async fn apply_group_changeset( + fn apply_group_changeset( &mut self, - changesets: &GroupChangesets, + changesets: &[GroupChangeset], ) -> FlowyResult<(Vec, TypeOptionData)>; + + /// Called before the row was created. + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str); } #[derive(Debug)] diff --git a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs index 0498f77eed..6134b7d265 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs @@ -1,11 +1,8 @@ -use std::collections::HashMap; use std::fmt::Formatter; use std::marker::PhantomData; use std::sync::Arc; -use async_trait::async_trait; use collab_database::fields::Field; -use collab_database::rows::{Cell, RowId}; use indexmap::IndexMap; use serde::de::DeserializeOwned; use serde::Serialize; @@ -21,28 +18,15 @@ use crate::services::group::{ default_group_setting, GeneratedGroups, Group, GroupChangeset, GroupData, GroupSetting, }; -pub trait GroupSettingReader: Send + Sync + 'static { +pub trait GroupContextDelegate: Send + Sync + 'static { fn get_group_setting(&self, view_id: &str) -> Fut>>; - fn get_configuration_cells(&self, view_id: &str, field_id: &str) -> Fut>; -} -pub trait GroupSettingWriter: Send + Sync + 'static { + fn get_configuration_cells(&self, view_id: &str, field_id: &str) -> Fut>; + fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut>; } -#[async_trait] -pub trait GroupTypeOptionCellOperation: Send + Sync + 'static { - async fn get_cell(&self, row_id: &RowId, field_id: &str) -> FlowyResult>; - async fn update_cell( - &self, - view_id: &str, - row_id: &RowId, - field_id: &str, - cell: Cell, - ) -> FlowyResult<()>; -} - -impl std::fmt::Display for GroupContext { +impl std::fmt::Display for GroupControllerContext { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.group_by_id.iter().for_each(|(_, group)| { let _ = f.write_fmt(format_args!( @@ -56,12 +40,12 @@ impl std::fmt::Display for GroupContext { } } -/// A [GroupContext] represents as the groups memory cache -/// Each [GenericGroupController] has its own [GroupContext], the `context` has its own configuration +/// A [GroupControllerContext] represents as the groups memory cache +/// Each [GenericGroupController] has its own [GroupControllerContext], the `context` has its own configuration /// that is restored from the disk. /// /// The `context` contains a list of [GroupData]s and the grouping [Field] -pub struct GroupContext { +pub struct GroupControllerContext { pub view_id: String, /// The group configuration restored from the disk. /// @@ -70,24 +54,18 @@ pub struct GroupContext { configuration_phantom: PhantomData, - /// The grouping field - field: Field, + /// The grouping field id + field_id: String, /// Cache all the groups. Cache the group by its id. /// We use the id of the [Field] as the [No Status] group id. group_by_id: IndexMap, - /// A reader that implement the [GroupSettingReader] trait - /// - reader: Arc, - - /// A writer that implement the [GroupSettingWriter] trait is used to save the - /// configuration to disk - /// - writer: Arc, + /// delegate that reads and writes data to and from disk + delegate: Arc, } -impl GroupContext +impl GroupControllerContext where C: Serialize + DeserializeOwned, { @@ -95,14 +73,13 @@ where pub async fn new( view_id: String, field: Field, - reader: Arc, - writer: Arc, + delegate: Arc, ) -> FlowyResult { - event!(tracing::Level::TRACE, "GroupContext::new"); - let setting = match reader.get_group_setting(&view_id).await { + event!(tracing::Level::TRACE, "GroupControllerContext::new"); + let setting = match delegate.get_group_setting(&view_id).await { None => { let default_configuration = default_group_setting(&field); - writer + delegate .save_configuration(&view_id, default_configuration.clone()) .await?; Arc::new(default_configuration) @@ -112,10 +89,9 @@ where Ok(Self { view_id, - field, + field_id: field.id, group_by_id: IndexMap::new(), - reader, - writer, + delegate, setting, configuration_phantom: PhantomData, }) @@ -126,11 +102,11 @@ where /// We take the `id` of the `field` as the no status group id #[allow(dead_code)] pub(crate) fn get_no_status_group(&self) -> Option<&GroupData> { - self.group_by_id.get(&self.field.id) + self.group_by_id.get(&self.field_id) } pub(crate) fn get_mut_no_status_group(&mut self) -> Option<&mut GroupData> { - self.group_by_id.get_mut(&self.field.id) + self.group_by_id.get_mut(&self.field_id) } pub(crate) fn groups(&self) -> Vec<&GroupData> { @@ -155,7 +131,7 @@ where /// Iterate mut the groups without `No status` group pub(crate) fn iter_mut_status_groups(&mut self, mut each: impl FnMut(&mut GroupData)) { self.group_by_id.iter_mut().for_each(|(_, group)| { - if group.id != self.field.id { + if group.id != self.field_id { each(group); } }); @@ -168,13 +144,7 @@ where } #[tracing::instrument(level = "trace", skip(self), err)] pub(crate) fn add_new_group(&mut self, group: Group) -> FlowyResult { - let group_data = GroupData::new( - group.id.clone(), - self.field.id.clone(), - group.name.clone(), - group.id.clone(), - group.visible, - ); + let group_data = GroupData::new(group.id.clone(), self.field_id.clone(), group.visible); self.group_by_id.insert(group.id.clone(), group_data); let (index, group_data) = self.get_group(&group.id).unwrap(); let insert_group = InsertedGroupPB { @@ -232,7 +202,7 @@ where configuration .groups .iter() - .map(|group| group.name.clone()) + .map(|group| group.id.clone()) .collect::>() .join(",") ); @@ -268,22 +238,12 @@ where ) -> FlowyResult> { let GeneratedGroups { no_status_group, - group_configs, + groups, } = generated_groups; - let mut new_groups = vec![]; - let mut filter_content_map = HashMap::new(); - group_configs.into_iter().for_each(|generate_group| { - filter_content_map.insert( - generate_group.group.id.clone(), - generate_group.filter_content, - ); - new_groups.push(generate_group.group); - }); - let mut old_groups = self.setting.groups.clone(); // clear all the groups if grouping by a new field - if self.setting.field_id != self.field.id { + if self.setting.field_id != self.field_id { old_groups.clear(); } @@ -292,7 +252,7 @@ where mut all_groups, new_groups, deleted_groups, - } = merge_groups(no_status_group, old_groups, new_groups); + } = merge_groups(no_status_group, old_groups, groups); let deleted_group_ids = deleted_groups .into_iter() @@ -321,12 +281,10 @@ where Some(pos) => { let old_group = configuration.groups.get_mut(pos).unwrap(); // Take the old group setting - group.visible = old_group.visible; - if !is_changed { - is_changed = is_group_changed(group, old_group); + if group.visible != old_group.visible { + is_changed = true; } - // Consider the the name of the `group_rev` as the newest. - old_group.name = group.name.clone(); + group.visible = old_group.visible; }, } } @@ -335,31 +293,14 @@ where // Update the memory cache of the groups all_groups.into_iter().for_each(|group| { - let filter_content = filter_content_map - .get(&group.id) - .cloned() - .unwrap_or_else(|| "".to_owned()); - let group = GroupData::new( - group.id, - self.field.id.clone(), - group.name, - filter_content, - group.visible, - ); + let group = GroupData::new(group.id, self.field_id.clone(), group.visible); self.group_by_id.insert(group.id.clone(), group); }); let initial_groups = new_groups .into_iter() .flat_map(|group_rev| { - let filter_content = filter_content_map.get(&group_rev.id)?; - let group = GroupData::new( - group_rev.id, - self.field.id.clone(), - group_rev.name, - filter_content.clone(), - group_rev.visible, - ); + let group = GroupData::new(group_rev.id, self.field_id.clone(), group_rev.visible); Some(GroupPB::from(group)) }) .collect(); @@ -385,14 +326,10 @@ where if let Some(visible) = group_changeset.visible { group.visible = visible; } - if let Some(name) = &group_changeset.name { - group.name = name.clone(); - } })?; if let Some(group) = update_group { if let Some(group_data) = self.group_by_id.get_mut(&group.id) { - group_data.name = group.name.clone(); group_data.is_visible = group.visible; }; } @@ -401,8 +338,8 @@ where pub(crate) async fn get_all_cells(&self) -> Vec { self - .reader - .get_configuration_cells(&self.view_id, &self.field.id) + .delegate + .get_configuration_cells(&self.view_id, &self.field_id) .await } @@ -423,10 +360,10 @@ where let is_changed = mut_configuration_fn(configuration); if is_changed { let configuration = (*self.setting).clone(); - let writer = self.writer.clone(); + let delegate = self.delegate.clone(); let view_id = self.view_id.clone(); af_spawn(async move { - match writer.save_configuration(&view_id, configuration).await { + match delegate.save_configuration(&view_id, configuration).await { Ok(_) => {}, Err(e) => { tracing::error!("Save group configuration failed: {}", e); @@ -504,13 +441,6 @@ fn merge_groups( merge_result } -fn is_group_changed(new: &Group, old: &Group) -> bool { - if new.name != old.name { - return true; - } - false -} - struct MergeGroupResult { // Contains the new groups and the updated groups all_groups: Vec, @@ -545,13 +475,13 @@ mod tests { exp_deleted_groups: Vec<&'a str>, } - let new_group = |name: &str| Group::new(name.to_string(), name.to_string()); + let new_group = |name: &str| Group::new(name.to_string()); let groups_from_strings = |strings: Vec<&str>| strings.iter().map(|s| new_group(s)).collect::>(); let group_stringify = |groups: Vec| { groups .iter() - .map(|group| group.name.clone()) + .map(|group| group.id.clone()) .collect::>() .join(",") }; diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs index 62396fb293..799ac394c8 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs @@ -1,13 +1,13 @@ use std::marker::PhantomData; +use std::sync::Arc; -use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{Cells, Row, RowDetail, RowId}; use futures::executor::block_on; use serde::de::DeserializeOwned; use serde::Serialize; -use flowy_error::FlowyResult; +use flowy_error::{FlowyError, FlowyResult}; use crate::entities::{ FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, @@ -16,67 +16,43 @@ use crate::entities::{ use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser}; use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData}; use crate::services::group::action::{ - DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize, + DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupController, GroupCustomize, }; -use crate::services::group::configuration::GroupContext; +use crate::services::group::configuration::GroupControllerContext; use crate::services::group::entities::GroupData; -use crate::services::group::{GroupChangeset, GroupChangesets, GroupsBuilder, MoveGroupRowContext}; +use crate::services::group::{GroupChangeset, GroupsBuilder, MoveGroupRowContext}; -// use collab_database::views::Group; +pub trait GroupControllerDelegate: Send + Sync + 'static { + fn get_field(&self, field_id: &str) -> Option; +} -/// The [GroupController] trait defines the group actions, including create/delete/move items -/// For example, the group will insert a item if the one of the new [RowRevision]'s [CellRevision]s -/// content match the group filter. -/// -/// Different [FieldType] has a different controller that implements the [GroupController] trait. -/// If the [FieldType] doesn't implement its group controller, then the [DefaultGroupController] will -/// be used. +/// [BaseGroupController] is a generic group controller that provides customized implementations +/// of the `GroupController` trait for different field types. /// -pub trait GroupController: GroupControllerOperation + Send + Sync { - /// Called when the type option of the [Field] was updated. - fn did_update_field_type_option(&mut self, field: &Field); - - /// Called before the row was created. - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str); -} - -#[async_trait] -pub trait GroupOperationInterceptor { - type GroupTypeOption: TypeOption; - async fn type_option_from_group_changeset( - &self, - _changeset: &GroupChangeset, - _type_option: &Self::GroupTypeOption, - _view_id: &str, - ) -> Option { - None - } -} - -/// C: represents the group configuration that impl [GroupConfigurationSerde] -/// T: the type-option data deserializer that impl [TypeOptionDataDeserializer] -/// G: the group generator, [GroupsBuilder] -/// P: the parser that impl [CellProtobufBlobParser] for the CellBytes -pub struct BaseGroupController { +/// - `C`: represents the group configuration that impl [GroupConfigurationSerde] +/// - `G`: group generator, [GroupsBuilder] +/// - `P`: parser that impl [CellProtobufBlobParser] for the CellBytes +/// +/// See also: [DefaultGroupController] which contains the most basic implementation of +/// `GroupController` that only has one group. +pub struct BaseGroupController { pub grouping_field_id: String, - pub type_option: T, - pub context: GroupContext, + pub context: GroupControllerContext, group_builder_phantom: PhantomData, cell_parser_phantom: PhantomData

, - pub operation_interceptor: I, + pub delegate: Arc, } -impl BaseGroupController +impl BaseGroupController where C: Serialize + DeserializeOwned, - T: TypeOption + From + Send + Sync, - G: GroupsBuilder, GroupTypeOption = T>, - I: GroupOperationInterceptor + Send + Sync, + T: TypeOption + Send + Sync, + G: GroupsBuilder, GroupTypeOption = T>, { pub async fn new( grouping_field: &Field, - mut configuration: GroupContext, - operation_interceptor: I, + mut configuration: GroupControllerContext, + delegate: Arc, ) -> FlowyResult { let field_type = FieldType::from(grouping_field.field_type); let type_option = grouping_field @@ -89,14 +65,20 @@ where Ok(Self { grouping_field_id: grouping_field.id.clone(), - type_option, context: configuration, group_builder_phantom: PhantomData, cell_parser_phantom: PhantomData, - operation_interceptor, + delegate, }) } + pub fn get_grouping_field_type_option(&self) -> Option { + self + .delegate + .get_field(&self.grouping_field_id) + .and_then(|field| field.get_type_option::(FieldType::from(field.field_type))) + } + fn update_no_status_group( &mut self, row_detail: &RowDetail, @@ -169,14 +151,12 @@ where } } -#[async_trait] -impl GroupControllerOperation for BaseGroupController +impl GroupController for BaseGroupController where P: CellProtobufBlobParser::CellProtobufType>, C: Serialize + DeserializeOwned + Sync + Send, - T: TypeOption + From + Send + Sync, - G: GroupsBuilder, GroupTypeOption = T>, - I: GroupOperationInterceptor + Send + Sync, + T: TypeOption + Send + Sync, + G: GroupsBuilder, GroupTypeOption = T>, Self: GroupCustomize, { fn field_id(&self) -> &str { @@ -204,7 +184,7 @@ where let mut grouped_rows: Vec = vec![]; let cell_data = ::CellData::from(&cell); for group in self.context.groups() { - if self.can_group(&group.filter_content, &cell_data) { + if self.can_group(&group.id, &cell_data) { grouped_rows.push(GroupedRow { row_detail: (*row_detail).clone(), group_id: group.id.clone(), @@ -236,7 +216,7 @@ where &mut self, name: String, ) -> FlowyResult<(Option, Option)> { - self.generate_new_group(name) + ::create_group(self, name) } fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> { @@ -260,7 +240,7 @@ where let mut suitable_group_ids = vec![]; for group in self.get_all_groups() { - if self.can_group(&group.filter_content, &cell_data) { + if self.can_group(&group.id, &cell_data) { suitable_group_ids.push(group.id.clone()); let changeset = GroupRowsNotificationPB::insert( group.id.clone(), @@ -384,7 +364,7 @@ where let cell_bytes = get_cell_protobuf(&cell, context.field, None); let cell_data = cell_bytes.parser::

()?; result.deleted_group = self.delete_group_when_move_row(&context.row_detail.row, &cell_data); - result.row_changesets = self.move_row(&cell_data, context); + result.row_changesets = self.move_row(context); } else { tracing::warn!("Unexpected moving group row, changes should not be empty"); } @@ -409,32 +389,30 @@ where .iter() .map(|row| row.row.id.clone()) .collect(); - let type_option_data = self.delete_group_custom(group_id)?; + let type_option_data = ::delete_group(self, group_id)?; Ok((row_ids, type_option_data)) }, None => Ok((vec![], None)), } } - async fn apply_group_changeset( + fn apply_group_changeset( &mut self, - changeset: &GroupChangesets, + changeset: &[GroupChangeset], ) -> FlowyResult<(Vec, TypeOptionData)> { - for group_changeset in changeset.changesets.iter() { + for group_changeset in changeset.iter() { self.context.update_group(group_changeset)?; } - let mut type_option_data = TypeOptionData::new(); - for group_changeset in changeset.changesets.iter() { - if let Some(new_type_option_data) = self - .operation_interceptor - .type_option_from_group_changeset(group_changeset, &self.type_option, &self.context.view_id) - .await - { - type_option_data.extend(new_type_option_data); - } + + let mut type_option = self.get_grouping_field_type_option().ok_or_else(|| { + FlowyError::internal().with_context("Failed to get grouping field type option") + })?; + + for group_changeset in changeset.iter() { + self.update_type_option_when_update_group(group_changeset, &mut type_option); } + let updated_groups = changeset - .changesets .iter() .filter_map(|changeset| { self @@ -442,7 +420,12 @@ where .map(|(_, group)| GroupPB::from(group)) }) .collect::>(); - Ok((updated_groups, type_option_data)) + + Ok((updated_groups, type_option.into())) + } + + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { + ::will_create_row(self, cells, field, group_id); } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs index e6e9fdeabd..a3057b24a0 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs @@ -10,11 +10,10 @@ use crate::services::field::{ CheckboxCellDataParser, CheckboxTypeOption, TypeOption, CHECK, UNCHECK, }; use crate::services::group::action::GroupCustomize; -use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::configuration::GroupControllerContext; +use crate::services::group::controller::BaseGroupController; use crate::services::group::{ - move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, GroupOperationInterceptor, - GroupsBuilder, MoveGroupRowContext, + move_group_row, GeneratedGroups, Group, GroupsBuilder, MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -22,16 +21,10 @@ pub struct CheckboxGroupConfiguration { pub hide_empty: bool, } -pub type CheckboxGroupController = BaseGroupController< - CheckboxGroupConfiguration, - CheckboxTypeOption, - CheckboxGroupBuilder, - CheckboxCellDataParser, - CheckboxGroupOperationInterceptorImpl, ->; - -pub type CheckboxGroupContext = GroupContext; +pub type CheckboxGroupController = + BaseGroupController; +pub type CheckboxGroupControllerContext = GroupControllerContext; impl GroupCustomize for CheckboxGroupController { type GroupTypeOption = CheckboxTypeOption; fn placeholder_cell(&self) -> Option { @@ -126,11 +119,7 @@ impl GroupCustomize for CheckboxGroupController { (None, changesets) } - fn move_row( - &mut self, - _cell_data: &::CellProtobufType, - mut context: MoveGroupRowContext, - ) -> Vec { + fn move_row(&mut self, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; self.context.iter_mut_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { @@ -140,17 +129,11 @@ impl GroupCustomize for CheckboxGroupController { group_changeset } - fn delete_group_custom(&mut self, _group_id: &str) -> FlowyResult> { + fn delete_group(&mut self, _group_id: &str) -> FlowyResult> { Ok(None) } -} -impl GroupController for CheckboxGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) { - // Do nothing - } - - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { None => tracing::warn!("Can not find the group: {}", group_id), Some((_, group)) => { @@ -165,7 +148,7 @@ impl GroupController for CheckboxGroupController { pub struct CheckboxGroupBuilder(); #[async_trait] impl GroupsBuilder for CheckboxGroupBuilder { - type Context = CheckboxGroupContext; + type Context = CheckboxGroupControllerContext; type GroupTypeOption = CheckboxTypeOption; async fn build( @@ -173,26 +156,12 @@ impl GroupsBuilder for CheckboxGroupBuilder { _context: &Self::Context, _type_option: &Self::GroupTypeOption, ) -> GeneratedGroups { - let check_group = GeneratedGroupConfig { - group: Group::new(CHECK.to_string(), "".to_string()), - filter_content: CHECK.to_string(), - }; - - let uncheck_group = GeneratedGroupConfig { - group: Group::new(UNCHECK.to_string(), "".to_string()), - filter_content: UNCHECK.to_string(), - }; + let check_group = Group::new(CHECK.to_string()); + let uncheck_group = Group::new(UNCHECK.to_string()); GeneratedGroups { no_status_group: None, - group_configs: vec![check_group, uncheck_group], + groups: vec![check_group, uncheck_group], } } } - -pub struct CheckboxGroupOperationInterceptorImpl {} - -#[async_trait] -impl GroupOperationInterceptor for CheckboxGroupOperationInterceptorImpl { - type GroupTypeOption = CheckboxTypeOption; -} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs index 1d947d66e3..8a2827a107 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs @@ -1,7 +1,5 @@ -use std::format; - use async_trait::async_trait; -use chrono::{DateTime, Datelike, Days, Duration, Local, NaiveDate, NaiveDateTime}; +use chrono::{DateTime, Datelike, Days, Duration, Local, NaiveDateTime}; use collab_database::database::timestamp; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; @@ -16,28 +14,24 @@ use crate::entities::{ use crate::services::cell::insert_date_cell; use crate::services::field::{DateCellData, DateCellDataParser, DateTypeOption, TypeOption}; use crate::services::group::action::GroupCustomize; -use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::configuration::GroupControllerContext; +use crate::services::group::controller::BaseGroupController; use crate::services::group::{ - make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, - GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, + make_no_status_group, move_group_row, GeneratedGroups, Group, GroupsBuilder, MoveGroupRowContext, }; -pub trait GroupConfigurationContentSerde: Sized + Send + Sync { - fn from_json(s: &str) -> Result; - fn to_json(&self) -> Result; -} - #[derive(Default, Serialize, Deserialize)] pub struct DateGroupConfiguration { pub hide_empty: bool, pub condition: DateCondition, } -impl GroupConfigurationContentSerde for DateGroupConfiguration { +impl DateGroupConfiguration { fn from_json(s: &str) -> Result { serde_json::from_str(s) } + + #[allow(dead_code)] fn to_json(&self) -> Result { serde_json::to_string(self) } @@ -54,15 +48,10 @@ pub enum DateCondition { Year = 4, } -pub type DateGroupController = BaseGroupController< - DateGroupConfiguration, - DateTypeOption, - DateGroupBuilder, - DateCellDataParser, - DateGroupOperationInterceptorImpl, ->; +pub type DateGroupController = + BaseGroupController; -pub type DateGroupContext = GroupContext; +pub type DateGroupControllerContext = GroupControllerContext; impl GroupCustomize for DateGroupController { type GroupTypeOption = DateTypeOption; @@ -80,7 +69,7 @@ impl GroupCustomize for DateGroupController { content: &str, cell_data: &::CellData, ) -> bool { - content == group_id(cell_data, &self.context.get_setting_content()) + content == get_date_group_id(cell_data, &self.context.get_setting_content()) } fn create_or_delete_group_when_cell_changed( @@ -93,7 +82,7 @@ impl GroupCustomize for DateGroupController { let mut inserted_group = None; if self .context - .get_group(&group_id(&_cell_data.into(), &setting_content)) + .get_group(&get_date_group_id(&_cell_data.into(), &setting_content)) .is_none() { let group = make_group_from_date_cell(&_cell_data.into(), &setting_content); @@ -106,7 +95,7 @@ impl GroupCustomize for DateGroupController { let deleted_group = match _old_cell_data.and_then(|old_cell_data| { self .context - .get_group(&group_id(&old_cell_data.into(), &setting_content)) + .get_group(&get_date_group_id(&old_cell_data.into(), &setting_content)) }) { None => None, Some((_, group)) => { @@ -138,7 +127,7 @@ impl GroupCustomize for DateGroupController { let setting_content = self.context.get_setting_content(); self.context.iter_mut_status_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); - if group.id == group_id(&cell_data.into(), &setting_content) { + if group.id == get_date_group_id(&cell_data.into(), &setting_content) { if !group.contains_row(&row_detail.row.id) { changeset .inserted_rows @@ -180,7 +169,7 @@ impl GroupCustomize for DateGroupController { let setting_content = self.context.get_setting_content(); let deleted_group = match self .context - .get_group(&group_id(cell_data, &setting_content)) + .get_group(&get_date_group_id(cell_data, &setting_content)) { Some((_, group)) if group.rows.len() == 1 => Some(group.clone()), _ => None, @@ -194,11 +183,7 @@ impl GroupCustomize for DateGroupController { (deleted_group, changesets) } - fn move_row( - &mut self, - _cell_data: &::CellProtobufType, - mut context: MoveGroupRowContext, - ) -> Vec { + fn move_row(&mut self, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; self.context.iter_mut_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { @@ -211,13 +196,13 @@ impl GroupCustomize for DateGroupController { fn delete_group_when_move_row( &mut self, _row: &Row, - _cell_data: &::CellProtobufType, + cell_data: &::CellProtobufType, ) -> Option { let mut deleted_group = None; let setting_content = self.context.get_setting_content(); if let Some((_, group)) = self .context - .get_group(&group_id(&_cell_data.into(), &setting_content)) + .get_group(&get_date_group_id(&cell_data.into(), &setting_content)) { if group.rows.len() == 1 { deleted_group = Some(GroupPB::from(group.clone())); @@ -229,16 +214,12 @@ impl GroupCustomize for DateGroupController { deleted_group } - fn delete_group_custom(&mut self, group_id: &str) -> FlowyResult> { + fn delete_group(&mut self, group_id: &str) -> FlowyResult> { self.context.delete_group(group_id)?; Ok(None) } -} -impl GroupController for DateGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) {} - - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { None => tracing::warn!("Can not find the group: {}", group_id), Some((_, _)) => { @@ -253,7 +234,7 @@ impl GroupController for DateGroupController { pub struct DateGroupBuilder(); #[async_trait] impl GroupsBuilder for DateGroupBuilder { - type Context = DateGroupContext; + type Context = DateGroupControllerContext; type GroupTypeOption = DateTypeOption; async fn build( @@ -265,39 +246,31 @@ impl GroupsBuilder for DateGroupBuilder { let cells = context.get_all_cells().await; // Generate the groups - let mut group_configs: Vec = cells + let mut groups: Vec = cells .into_iter() .flat_map(|value| value.into_date_field_cell_data()) .filter(|cell| cell.timestamp.is_some()) - .map(|cell| { - let group = make_group_from_date_cell(&cell, &context.get_setting_content()); - GeneratedGroupConfig { - filter_content: group.id.clone(), - group, - } - }) + .map(|cell| make_group_from_date_cell(&cell, &context.get_setting_content())) .collect(); - group_configs.sort_by(|a, b| a.filter_content.cmp(&b.filter_content)); + groups.sort_by(|a, b| a.id.cmp(&b.id)); let no_status_group = Some(make_no_status_group(field)); + GeneratedGroups { no_status_group, - group_configs, + groups, } } } fn make_group_from_date_cell(cell_data: &DateCellData, setting_content: &str) -> Group { - let group_id = group_id(cell_data, setting_content); - Group::new( - group_id.clone(), - group_name_from_id(&group_id, setting_content), - ) + let group_id = get_date_group_id(cell_data, setting_content); + Group::new(group_id) } const GROUP_ID_DATE_FORMAT: &str = "%Y/%m/%d"; -fn group_id(cell_data: &DateCellData, setting_content: &str) -> String { +fn get_date_group_id(cell_data: &DateCellData, setting_content: &str) -> String { let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default(); let date_time = date_time_from_timestamp(cell_data.timestamp); @@ -354,63 +327,6 @@ fn group_id(cell_data: &DateCellData, setting_content: &str) -> String { date.to_string() } -fn group_name_from_id(group_id: &str, setting_content: &str) -> String { - let config = DateGroupConfiguration::from_json(setting_content).unwrap_or_default(); - let date = NaiveDate::parse_from_str(group_id, GROUP_ID_DATE_FORMAT).unwrap(); - - let tmp; - match config.condition { - DateCondition::Day => { - tmp = format!("{} {}, {}", date.format("%b"), date.day(), date.year(),); - tmp - }, - DateCondition::Week => { - let begin_of_week = date - .checked_sub_days(Days::new(date.weekday().num_days_from_monday() as u64)) - .unwrap() - .format("%d"); - let end_of_week = date - .checked_add_days(Days::new(6 - date.weekday().num_days_from_monday() as u64)) - .unwrap() - .format("%d"); - - tmp = format!( - "Week of {} {}-{} {}", - date.format("%b"), - begin_of_week, - end_of_week, - date.year() - ); - tmp - }, - DateCondition::Month => { - tmp = format!("{} {}", date.format("%b"), date.year(),); - tmp - }, - DateCondition::Year => date.year().to_string(), - DateCondition::Relative => { - let now = date_time_from_timestamp(Some(timestamp())); - - let diff = date.signed_duration_since(now.date_naive()); - let result = match diff.num_days() { - 0 => "Today", - -1 => "Yesterday", - 1 => "Tomorrow", - -7 => "Last 7 days", - 2 => "Next 7 days", - -30 => "Last 30 days", - 8 => "Next 30 days", - _ => { - tmp = format!("{} {}", date.format("%b"), date.year(),); - &tmp - }, - }; - - result.to_string() - }, - } -} - fn date_time_from_timestamp(timestamp: Option) -> DateTime { match timestamp { Some(timestamp) => { @@ -423,24 +339,14 @@ fn date_time_from_timestamp(timestamp: Option) -> DateTime { } } -pub struct DateGroupOperationInterceptorImpl {} - -#[async_trait] -impl GroupOperationInterceptor for DateGroupOperationInterceptorImpl { - type GroupTypeOption = DateTypeOption; -} - #[cfg(test)] mod tests { - use std::vec; - use chrono::{offset, Days, Duration, NaiveDateTime}; - use crate::services::{ - field::{date_type_option::DateTypeOption, DateCellData}, - group::controller_impls::date_controller::{ - group_id, group_name_from_id, GROUP_ID_DATE_FORMAT, - }, + use crate::services::field::date_type_option::DateTypeOption; + use crate::services::field::DateCellData; + use crate::services::group::controller_impls::date_controller::{ + get_date_group_id, GROUP_ID_DATE_FORMAT, }; #[test] @@ -449,7 +355,6 @@ mod tests { cell_data: DateCellData, setting_content: String, exp_group_id: String, - exp_group_name: String, } let mar_14_2022 = NaiveDateTime::from_timestamp_opt(1647251762, 0).unwrap(); @@ -471,7 +376,6 @@ mod tests { cell_data: mar_14_2022_cd.clone(), setting_content: r#"{"condition": 0, "hide_empty": false}"#.to_string(), exp_group_id: "2022/03/01".to_string(), - exp_group_name: "Mar 2022".to_string(), }, GroupIDTest { cell_data: DateCellData { @@ -481,7 +385,6 @@ mod tests { }, setting_content: r#"{"condition": 0, "hide_empty": false}"#.to_string(), exp_group_id: today.format(GROUP_ID_DATE_FORMAT).to_string(), - exp_group_name: "Today".to_string(), }, GroupIDTest { cell_data: DateCellData { @@ -495,13 +398,11 @@ mod tests { .unwrap() .format(GROUP_ID_DATE_FORMAT) .to_string(), - exp_group_name: "Last 7 days".to_string(), }, GroupIDTest { cell_data: mar_14_2022_cd.clone(), setting_content: r#"{"condition": 1, "hide_empty": false}"#.to_string(), exp_group_id: "2022/03/14".to_string(), - exp_group_name: "Mar 14, 2022".to_string(), }, GroupIDTest { cell_data: DateCellData { @@ -516,19 +417,16 @@ mod tests { }, setting_content: r#"{"condition": 2, "hide_empty": false}"#.to_string(), exp_group_id: "2022/03/14".to_string(), - exp_group_name: "Week of Mar 14-20 2022".to_string(), }, GroupIDTest { cell_data: mar_14_2022_cd.clone(), setting_content: r#"{"condition": 3, "hide_empty": false}"#.to_string(), exp_group_id: "2022/03/01".to_string(), - exp_group_name: "Mar 2022".to_string(), }, GroupIDTest { cell_data: mar_14_2022_cd, setting_content: r#"{"condition": 4, "hide_empty": false}"#.to_string(), exp_group_id: "2022/01/01".to_string(), - exp_group_name: "2022".to_string(), }, GroupIDTest { cell_data: DateCellData { @@ -538,7 +436,6 @@ mod tests { }, setting_content: r#"{"condition": 1, "hide_empty": false}"#.to_string(), exp_group_id: "2023/06/02".to_string(), - exp_group_name: "".to_string(), }, GroupIDTest { cell_data: DateCellData { @@ -548,18 +445,12 @@ mod tests { }, setting_content: r#"{"condition": 1, "hide_empty": false}"#.to_string(), exp_group_id: "2023/06/03".to_string(), - exp_group_name: "".to_string(), }, ]; for (i, test) in tests.iter().enumerate() { - let group_id = group_id(&test.cell_data, &test.setting_content); + let group_id = get_date_group_id(&test.cell_data, &test.setting_content); assert_eq!(test.exp_group_id, group_id, "test {}", i); - - if !test.exp_group_name.is_empty() { - let group_name = group_name_from_id(&group_id, &test.setting_content); - assert_eq!(test.exp_group_name, group_name, "test {}", i); - } } } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs index ba54c6e4b0..a921c48f2c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs @@ -8,9 +8,9 @@ use crate::entities::{ GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, }; use crate::services::group::action::{ - DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, + DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupController, }; -use crate::services::group::{GroupChangesets, GroupController, GroupData, MoveGroupRowContext}; +use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext}; /// A [DefaultGroupController] is used to handle the group actions for the [FieldType] that doesn't /// implement its own group controller. The default group controller only contains one group, which @@ -25,13 +25,7 @@ const DEFAULT_GROUP_CONTROLLER: &str = "DefaultGroupController"; impl DefaultGroupController { pub fn new(field: &Field) -> Self { - let group = GroupData::new( - DEFAULT_GROUP_CONTROLLER.to_owned(), - field.id.clone(), - "".to_owned(), - "".to_owned(), - true, - ); + let group = GroupData::new(DEFAULT_GROUP_CONTROLLER.to_owned(), field.id.clone(), true); Self { field_id: field.id.clone(), group, @@ -40,7 +34,7 @@ impl DefaultGroupController { } #[async_trait] -impl GroupControllerOperation for DefaultGroupController { +impl GroupController for DefaultGroupController { fn field_id(&self) -> &str { &self.field_id } @@ -131,18 +125,12 @@ impl GroupControllerOperation for DefaultGroupController { Ok((vec![], None)) } - async fn apply_group_changeset( + fn apply_group_changeset( &mut self, - _changeset: &GroupChangesets, + _changeset: &[GroupChangeset], ) -> FlowyResult<(Vec, TypeOptionData)> { Ok((Vec::new(), TypeOptionData::default())) } -} -impl GroupController for DefaultGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) { - // Do nothing - } - - fn will_create_row(&mut self, _cells: &mut Cells, _field: &Field, _group_id: &str) {} + fn will_create_row(&self, _cells: &mut Cells, _field: &Field, _group_id: &str) {} } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index dfc7ce8ce9..af160a86ea 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; -use flowy_error::FlowyResult; +use flowy_error::{FlowyError, FlowyResult}; use serde::{Deserialize, Serialize}; use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; @@ -11,11 +11,11 @@ use crate::services::field::{ TypeOption, }; use crate::services::group::action::GroupCustomize; -use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::controller::BaseGroupController; use crate::services::group::{ add_or_remove_select_option_row, generate_select_option_groups, make_no_status_group, - move_group_row, remove_select_option_row, GeneratedGroups, Group, GroupChangeset, GroupContext, - GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, + move_group_row, remove_select_option_row, GeneratedGroups, Group, GroupChangeset, + GroupControllerContext, GroupsBuilder, MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -23,14 +23,12 @@ pub struct MultiSelectGroupConfiguration { pub hide_empty: bool, } -pub type MultiSelectOptionGroupContext = GroupContext; +pub type MultiSelectGroupControllerContext = GroupControllerContext; // MultiSelect pub type MultiSelectGroupController = BaseGroupController< MultiSelectGroupConfiguration, - MultiSelectTypeOption, MultiSelectGroupBuilder, SelectOptionCellDataParser, - MultiSelectGroupOperationInterceptorImpl, >; impl GroupCustomize for MultiSelectGroupController { @@ -80,11 +78,7 @@ impl GroupCustomize for MultiSelectGroupController { (None, changesets) } - fn move_row( - &mut self, - _cell_data: &::CellProtobufType, - mut context: MoveGroupRowContext, - ) -> Vec { + fn move_row(&mut self, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; self.context.iter_mut_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { @@ -94,83 +88,44 @@ impl GroupCustomize for MultiSelectGroupController { group_changeset } - fn generate_new_group( + fn create_group( &mut self, name: String, ) -> FlowyResult<(Option, Option)> { - let mut new_type_option = self.type_option.clone(); - let new_select_option = self.type_option.create_option(&name); + let mut new_type_option = self.get_grouping_field_type_option().ok_or_else(|| { + FlowyError::internal().with_context("Failed to get grouping field type option") + })?; + let new_select_option = new_type_option.create_option(&name); new_type_option.insert_option(new_select_option.clone()); - let new_group = Group::new(new_select_option.id, new_select_option.name); + let new_group = Group::new(new_select_option.id); let inserted_group_pb = self.context.add_new_group(new_group)?; Ok((Some(new_type_option.into()), Some(inserted_group_pb))) } - fn delete_group_custom(&mut self, group_id: &str) -> FlowyResult> { - if let Some(option_index) = self - .type_option + fn delete_group(&mut self, group_id: &str) -> FlowyResult> { + let mut new_type_option = self.get_grouping_field_type_option().ok_or_else(|| { + FlowyError::internal().with_context("Failed to get grouping field type option") + })?; + if let Some(option_index) = new_type_option .options .iter() .position(|option| option.id == group_id) { // Remove the option if the group is found - let mut new_type_option = self.type_option.clone(); new_type_option.options.remove(option_index); Ok(Some(new_type_option.into())) } else { Ok(None) } } -} -impl GroupController for MultiSelectGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) {} - - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { - match self.context.get_group(group_id) { - None => tracing::warn!("Can not find the group: {}", group_id), - Some((_, group)) => { - let cell = insert_select_option_cell(vec![group.id.clone()], field); - cells.insert(field.id.clone(), cell); - }, - } - } -} - -pub struct MultiSelectGroupBuilder; -#[async_trait] -impl GroupsBuilder for MultiSelectGroupBuilder { - type Context = MultiSelectOptionGroupContext; - type GroupTypeOption = MultiSelectTypeOption; - - async fn build( - field: &Field, - _context: &Self::Context, - type_option: &Self::GroupTypeOption, - ) -> GeneratedGroups { - let group_configs = generate_select_option_groups(&field.id, &type_option.options); - GeneratedGroups { - no_status_group: Some(make_no_status_group(field)), - group_configs, - } - } -} - -pub struct MultiSelectGroupOperationInterceptorImpl; - -#[async_trait] -impl GroupOperationInterceptor for MultiSelectGroupOperationInterceptorImpl { - type GroupTypeOption = MultiSelectTypeOption; - - #[tracing::instrument(level = "trace", skip_all)] - async fn type_option_from_group_changeset( - &self, + fn update_type_option_when_update_group( + &mut self, changeset: &GroupChangeset, - type_option: &Self::GroupTypeOption, - _view_id: &str, - ) -> Option { + type_option: &mut Self::GroupTypeOption, + ) { if let Some(name) = &changeset.name { let mut new_type_option = type_option.clone(); let select_option = type_option @@ -184,9 +139,36 @@ impl GroupOperationInterceptor for MultiSelectGroupOperationInterceptorImpl { ..select_option.to_owned() }; new_type_option.insert_option(new_select_option); - return Some(new_type_option.into()); } + } - None + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { + match self.context.get_group(group_id) { + None => tracing::warn!("Can not find the group: {}", group_id), + Some((_index, group)) => { + let cell = insert_select_option_cell(vec![group.id.clone()], field); + cells.insert(field.id.clone(), cell); + }, + } + } +} + +pub struct MultiSelectGroupBuilder; +#[async_trait] +impl GroupsBuilder for MultiSelectGroupBuilder { + type Context = MultiSelectGroupControllerContext; + type GroupTypeOption = MultiSelectTypeOption; + + async fn build( + field: &Field, + _context: &Self::Context, + type_option: &Self::GroupTypeOption, + ) -> GeneratedGroups { + let groups = generate_select_option_groups(&field.id, &type_option.options); + + GeneratedGroups { + no_status_group: Some(make_no_status_group(field)), + groups, + } } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index 6986ad0e83..fc23364f4d 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; -use flowy_error::FlowyResult; +use flowy_error::{FlowyError, FlowyResult}; use serde::{Deserialize, Serialize}; use crate::entities::{FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; @@ -11,12 +11,11 @@ use crate::services::field::{ TypeOption, }; use crate::services::group::action::GroupCustomize; -use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::controller::BaseGroupController; use crate::services::group::controller_impls::select_option_controller::util::*; -use crate::services::group::entities::GroupData; use crate::services::group::{ - make_no_status_group, GeneratedGroups, Group, GroupChangeset, GroupContext, - GroupOperationInterceptor, GroupsBuilder, MoveGroupRowContext, + make_no_status_group, GeneratedGroups, Group, GroupChangeset, GroupControllerContext, + GroupsBuilder, MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -24,19 +23,19 @@ pub struct SingleSelectGroupConfiguration { pub hide_empty: bool, } -pub type SingleSelectOptionGroupContext = GroupContext; +pub type SingleSelectGroupControllerContext = + GroupControllerContext; // SingleSelect pub type SingleSelectGroupController = BaseGroupController< SingleSelectGroupConfiguration, - SingleSelectTypeOption, SingleSelectGroupBuilder, SelectOptionCellDataParser, - SingleSelectGroupOperationInterceptorImpl, >; impl GroupCustomize for SingleSelectGroupController { type GroupTypeOption = SingleSelectTypeOption; + fn can_group( &self, content: &str, @@ -81,11 +80,7 @@ impl GroupCustomize for SingleSelectGroupController { (None, changesets) } - fn move_row( - &mut self, - _cell_data: &::CellProtobufType, - mut context: MoveGroupRowContext, - ) -> Vec { + fn move_row(&mut self, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; self.context.iter_mut_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { @@ -95,85 +90,44 @@ impl GroupCustomize for SingleSelectGroupController { group_changeset } - fn generate_new_group( + fn create_group( &mut self, name: String, ) -> FlowyResult<(Option, Option)> { - let mut new_type_option = self.type_option.clone(); - let new_select_option = self.type_option.create_option(&name); + let mut new_type_option = self.get_grouping_field_type_option().ok_or_else(|| { + FlowyError::internal().with_context("Failed to get grouping field type option") + })?; + let new_select_option = new_type_option.create_option(&name); new_type_option.insert_option(new_select_option.clone()); - let new_group = Group::new(new_select_option.id, new_select_option.name); + let new_group = Group::new(new_select_option.id); let inserted_group_pb = self.context.add_new_group(new_group)?; Ok((Some(new_type_option.into()), Some(inserted_group_pb))) } - fn delete_group_custom(&mut self, group_id: &str) -> FlowyResult> { - if let Some(option_index) = self - .type_option + fn delete_group(&mut self, group_id: &str) -> FlowyResult> { + let mut new_type_option = self.get_grouping_field_type_option().ok_or_else(|| { + FlowyError::internal().with_context("Failed to get grouping field type option") + })?; + if let Some(option_index) = new_type_option .options .iter() .position(|option| option.id == group_id) { // Remove the option if the group is found - let mut new_type_option = self.type_option.clone(); new_type_option.options.remove(option_index); Ok(Some(new_type_option.into())) } else { - // Return None if no matching group is found Ok(None) } } -} -impl GroupController for SingleSelectGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) {} - - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { - let group: Option<&mut GroupData> = self.context.get_mut_group(group_id); - match group { - None => {}, - Some(group) => { - let cell = insert_select_option_cell(vec![group.id.clone()], field); - cells.insert(field.id.clone(), cell); - }, - } - } -} - -pub struct SingleSelectGroupBuilder(); -#[async_trait] -impl GroupsBuilder for SingleSelectGroupBuilder { - type Context = SingleSelectOptionGroupContext; - type GroupTypeOption = SingleSelectTypeOption; - async fn build( - field: &Field, - _context: &Self::Context, - type_option: &Self::GroupTypeOption, - ) -> GeneratedGroups { - let group_configs = generate_select_option_groups(&field.id, &type_option.options); - - GeneratedGroups { - no_status_group: Some(make_no_status_group(field)), - group_configs, - } - } -} - -pub struct SingleSelectGroupOperationInterceptorImpl; - -#[async_trait] -impl GroupOperationInterceptor for SingleSelectGroupOperationInterceptorImpl { - type GroupTypeOption = SingleSelectTypeOption; - - #[tracing::instrument(level = "trace", skip_all)] - async fn type_option_from_group_changeset( - &self, + fn update_type_option_when_update_group( + &mut self, changeset: &GroupChangeset, - type_option: &Self::GroupTypeOption, - _view_id: &str, - ) -> Option { + type_option: &mut Self::GroupTypeOption, + ) { if let Some(name) = &changeset.name { let mut new_type_option = type_option.clone(); let select_option = type_option @@ -187,9 +141,35 @@ impl GroupOperationInterceptor for SingleSelectGroupOperationInterceptorImpl { ..select_option.to_owned() }; new_type_option.insert_option(new_select_option); - return Some(new_type_option.into()); } + } - None + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { + match self.context.get_group(group_id) { + None => tracing::warn!("Can not find the group: {}", group_id), + Some((_index, group)) => { + let cell = insert_select_option_cell(vec![group.id.clone()], field); + cells.insert(field.id.clone(), cell); + }, + } + } +} + +pub struct SingleSelectGroupBuilder(); +#[async_trait] +impl GroupsBuilder for SingleSelectGroupBuilder { + type Context = SingleSelectGroupControllerContext; + type GroupTypeOption = SingleSelectTypeOption; + async fn build( + field: &Field, + _context: &Self::Context, + type_option: &Self::GroupTypeOption, + ) -> GeneratedGroups { + let groups = generate_select_option_groups(&field.id, &type_option.options); + + GeneratedGroups { + no_status_group: Some(make_no_status_group(field)), + groups, + } } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs index b8a144b594..01bd4cdc0d 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/util.rs @@ -9,7 +9,7 @@ use crate::services::cell::{ insert_checkbox_cell, insert_date_cell, insert_select_option_cell, insert_url_cell, }; use crate::services::field::{SelectOption, SelectOptionIds, CHECK}; -use crate::services::group::{GeneratedGroupConfig, Group, GroupData, MoveGroupRowContext}; +use crate::services::group::{Group, GroupData, MoveGroupRowContext}; pub fn add_or_remove_select_option_row( group: &mut GroupData, @@ -186,16 +186,10 @@ pub fn make_inserted_cell(group_id: &str, field: &Field) -> Option { } } -pub fn generate_select_option_groups( - _field_id: &str, - options: &[SelectOption], -) -> Vec { +pub fn generate_select_option_groups(_field_id: &str, options: &[SelectOption]) -> Vec { let groups = options .iter() - .map(|option| GeneratedGroupConfig { - group: Group::new(option.id.clone(), option.name.clone()), - filter_content: option.id.clone(), - }) + .map(|option| Group::new(option.id.clone())) .collect(); groups diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs index a687acce5d..195bae405c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use async_trait::async_trait; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail}; @@ -13,11 +11,10 @@ use crate::entities::{ use crate::services::cell::insert_url_cell; use crate::services::field::{TypeOption, URLCellData, URLCellDataParser, URLTypeOption}; use crate::services::group::action::GroupCustomize; -use crate::services::group::configuration::GroupContext; -use crate::services::group::controller::{BaseGroupController, GroupController}; +use crate::services::group::configuration::GroupControllerContext; +use crate::services::group::controller::BaseGroupController; use crate::services::group::{ - make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group, - GroupOperationInterceptor, GroupTypeOptionCellOperation, GroupsBuilder, MoveGroupRowContext, + make_no_status_group, move_group_row, GeneratedGroups, Group, GroupsBuilder, MoveGroupRowContext, }; #[derive(Default, Serialize, Deserialize)] @@ -25,15 +22,10 @@ pub struct URLGroupConfiguration { pub hide_empty: bool, } -pub type URLGroupController = BaseGroupController< - URLGroupConfiguration, - URLTypeOption, - URLGroupGenerator, - URLCellDataParser, - URLGroupOperationInterceptorImpl, ->; +pub type URLGroupController = + BaseGroupController; -pub type URLGroupContext = GroupContext; +pub type URLGroupControllerContext = GroupControllerContext; impl GroupCustomize for URLGroupController { type GroupTypeOption = URLTypeOption; @@ -64,7 +56,7 @@ impl GroupCustomize for URLGroupController { let mut inserted_group = None; if self.context.get_group(&_cell_data.url).is_none() { let cell_data: URLCellData = _cell_data.clone().into(); - let group = make_group_from_url_cell(&cell_data); + let group = Group::new(cell_data.data); let mut new_group = self.context.add_new_group(group)?; new_group.group.rows.push(RowMetaPB::from(_row_detail)); inserted_group = Some(new_group); @@ -155,11 +147,7 @@ impl GroupCustomize for URLGroupController { (deleted_group, changesets) } - fn move_row( - &mut self, - _cell_data: &::CellProtobufType, - mut context: MoveGroupRowContext, - ) -> Vec { + fn move_row(&mut self, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; self.context.iter_mut_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { @@ -168,13 +156,14 @@ impl GroupCustomize for URLGroupController { }); group_changeset } + fn delete_group_when_move_row( &mut self, _row: &Row, - _cell_data: &::CellProtobufType, + cell_data: &::CellProtobufType, ) -> Option { let mut deleted_group = None; - if let Some((_, group)) = self.context.get_group(&_cell_data.content) { + if let Some((_index, group)) = self.context.get_group(&cell_data.content) { if group.rows.len() == 1 { deleted_group = Some(GroupPB::from(group.clone())); } @@ -185,16 +174,12 @@ impl GroupCustomize for URLGroupController { deleted_group } - fn delete_group_custom(&mut self, group_id: &str) -> FlowyResult> { + fn delete_group(&mut self, group_id: &str) -> FlowyResult> { self.context.delete_group(group_id)?; Ok(None) } -} -impl GroupController for URLGroupController { - fn did_update_field_type_option(&mut self, _field: &Field) {} - - fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) { + fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) { match self.context.get_group(group_id) { None => tracing::warn!("Can not find the group: {}", group_id), Some((_, group)) => { @@ -208,7 +193,7 @@ impl GroupController for URLGroupController { pub struct URLGroupGenerator(); #[async_trait] impl GroupsBuilder for URLGroupGenerator { - type Context = URLGroupContext; + type Context = URLGroupControllerContext; type GroupTypeOption = URLTypeOption; async fn build( @@ -220,36 +205,18 @@ impl GroupsBuilder for URLGroupGenerator { let cells = context.get_all_cells().await; // Generate the groups - let group_configs = cells + let groups = cells .into_iter() .flat_map(|value| value.into_url_field_cell_data()) .filter(|cell| !cell.data.is_empty()) - .map(|cell| GeneratedGroupConfig { - group: make_group_from_url_cell(&cell), - filter_content: cell.data, - }) + .map(|cell| Group::new(cell.data.clone())) .collect(); let no_status_group = Some(make_no_status_group(field)); + GeneratedGroups { no_status_group, - group_configs, + groups, } } } - -fn make_group_from_url_cell(cell: &URLCellData) -> Group { - let group_id = cell.data.clone(); - let group_name = cell.data.clone(); - Group::new(group_id, group_name) -} - -pub struct URLGroupOperationInterceptorImpl { - #[allow(dead_code)] - pub(crate) cell_writer: Arc, -} - -#[async_trait::async_trait] -impl GroupOperationInterceptor for URLGroupOperationInterceptorImpl { - type GroupTypeOption = URLTypeOption; -} diff --git a/frontend/rust-lib/flowy-database2/src/services/group/entities.rs b/frontend/rust-lib/flowy-database2/src/services/group/entities.rs index 253c12bac9..12692fd812 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/entities.rs @@ -14,16 +14,6 @@ pub struct GroupSetting { pub content: String, } -pub struct GroupChangesets { - pub changesets: Vec, -} - -impl From> for GroupChangesets { - fn from(changesets: Vec) -> Self { - Self { changesets } - } -} - #[derive(Clone, Default, Debug)] pub struct GroupChangeset { pub group_id: String, @@ -92,7 +82,6 @@ impl From for GroupSettingMap { #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Group { pub id: String, - pub name: String, #[serde(default = "GROUP_VISIBILITY")] pub visible: bool, } @@ -104,9 +93,8 @@ impl TryFrom for Group { match value.get_str_value("id") { None => bail!("Invalid group data"), Some(id) => { - let name = value.get_str_value("name").unwrap_or_default(); let visible = value.get_bool_value("visible").unwrap_or_default(); - Ok(Self { id, name, visible }) + Ok(Self { id, visible }) }, } } @@ -116,7 +104,6 @@ impl From for GroupMap { fn from(group: Group) -> Self { GroupMapBuilder::new() .insert_str_value("id", group.id) - .insert_str_value("name", group.name) .insert_bool_value("visible", group.visible) .build() } @@ -125,12 +112,8 @@ impl From for GroupMap { const GROUP_VISIBILITY: fn() -> bool = || true; impl Group { - pub fn new(id: String, name: String) -> Self { - Self { - id, - name, - visible: true, - } + pub fn new(id: String) -> Self { + Self { id, visible: true } } } @@ -138,32 +121,20 @@ impl Group { pub struct GroupData { pub id: String, pub field_id: String, - pub name: String, pub is_default: bool, pub is_visible: bool, pub(crate) rows: Vec, - - /// [filter_content] is used to determine which group the cell belongs to. - pub filter_content: String, } impl GroupData { - pub fn new( - id: String, - field_id: String, - name: String, - filter_content: String, - is_visible: bool, - ) -> Self { + pub fn new(id: String, field_id: String, is_visible: bool) -> Self { let is_default = id == field_id; Self { id, field_id, is_default, is_visible, - name, rows: vec![], - filter_content, } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs b/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs index 1c6c372e81..7c11851528 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/group_builder.rs @@ -4,21 +4,17 @@ use std::sync::Arc; use async_trait::async_trait; use collab_database::fields::Field; use collab_database::rows::{Cell, RowDetail, RowId}; -use collab_database::views::DatabaseLayout; use flowy_error::FlowyResult; use crate::entities::FieldType; use crate::services::field::TypeOption; use crate::services::group::{ - CheckboxGroupContext, CheckboxGroupController, CheckboxGroupOperationInterceptorImpl, - DateGroupContext, DateGroupController, DateGroupOperationInterceptorImpl, DefaultGroupController, - Group, GroupController, GroupSetting, GroupSettingReader, GroupSettingWriter, - GroupTypeOptionCellOperation, MultiSelectGroupController, - MultiSelectGroupOperationInterceptorImpl, MultiSelectOptionGroupContext, - SingleSelectGroupController, SingleSelectGroupOperationInterceptorImpl, - SingleSelectOptionGroupContext, URLGroupContext, URLGroupController, - URLGroupOperationInterceptorImpl, + CheckboxGroupController, CheckboxGroupControllerContext, DateGroupController, + DateGroupControllerContext, DefaultGroupController, Group, GroupContextDelegate, GroupController, + GroupControllerDelegate, GroupSetting, MultiSelectGroupController, + MultiSelectGroupControllerContext, SingleSelectGroupController, + SingleSelectGroupControllerContext, URLGroupController, URLGroupControllerContext, }; /// The [GroupsBuilder] trait is used to generate the groups for different [FieldType] @@ -36,12 +32,7 @@ pub trait GroupsBuilder: Send + Sync + 'static { pub struct GeneratedGroups { pub no_status_group: Option, - pub group_configs: Vec, -} - -pub struct GeneratedGroupConfig { - pub group: Group, - pub filter_content: String, + pub groups: Vec, } pub struct MoveGroupRowContext<'a> { @@ -94,95 +85,56 @@ impl RowChangeset { fields(grouping_field_id=%grouping_field.id, grouping_field_type) err )] -pub async fn make_group_controller( +pub async fn make_group_controller( view_id: String, grouping_field: Field, row_details: Vec>, - setting_reader: R, - setting_writer: W, - type_option_cell_writer: TW, + delegate: D, ) -> FlowyResult> where - R: GroupSettingReader, - W: GroupSettingWriter, - TW: GroupTypeOptionCellOperation, + D: GroupContextDelegate + GroupControllerDelegate, { let grouping_field_type = FieldType::from(grouping_field.field_type); tracing::Span::current().record("grouping_field", &grouping_field_type.default_name()); let mut group_controller: Box; - let configuration_reader = Arc::new(setting_reader); - let configuration_writer = Arc::new(setting_writer); - let type_option_cell_writer = Arc::new(type_option_cell_writer); + let delegate = Arc::new(delegate); match grouping_field_type { FieldType::SingleSelect => { - let configuration = SingleSelectOptionGroupContext::new( - view_id, - grouping_field.clone(), - configuration_reader, - configuration_writer, - ) - .await?; - let operation_interceptor = SingleSelectGroupOperationInterceptorImpl; - let controller = - SingleSelectGroupController::new(&grouping_field, configuration, operation_interceptor) + let configuration = + SingleSelectGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()) .await?; + let controller = + SingleSelectGroupController::new(&grouping_field, configuration, delegate).await?; group_controller = Box::new(controller); }, FieldType::MultiSelect => { - let configuration = MultiSelectOptionGroupContext::new( - view_id, - grouping_field.clone(), - configuration_reader, - configuration_writer, - ) - .await?; - let operation_interceptor = MultiSelectGroupOperationInterceptorImpl; - let controller = - MultiSelectGroupController::new(&grouping_field, configuration, operation_interceptor) + let configuration = + MultiSelectGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()) .await?; + let controller = + MultiSelectGroupController::new(&grouping_field, configuration, delegate).await?; group_controller = Box::new(controller); }, FieldType::Checkbox => { - let configuration = CheckboxGroupContext::new( - view_id, - grouping_field.clone(), - configuration_reader, - configuration_writer, - ) - .await?; - let operation_interceptor = CheckboxGroupOperationInterceptorImpl {}; + let configuration = + CheckboxGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()) + .await?; let controller = - CheckboxGroupController::new(&grouping_field, configuration, operation_interceptor).await?; + CheckboxGroupController::new(&grouping_field, configuration, delegate).await?; group_controller = Box::new(controller); }, FieldType::URL => { - let configuration = URLGroupContext::new( - view_id, - grouping_field.clone(), - configuration_reader, - configuration_writer, - ) - .await?; - let operation_interceptor = URLGroupOperationInterceptorImpl { - cell_writer: type_option_cell_writer, - }; - let controller = - URLGroupController::new(&grouping_field, configuration, operation_interceptor).await?; + let configuration = + URLGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()).await?; + let controller = URLGroupController::new(&grouping_field, configuration, delegate).await?; group_controller = Box::new(controller); }, FieldType::DateTime => { - let configuration = DateGroupContext::new( - view_id, - grouping_field.clone(), - configuration_reader, - configuration_writer, - ) - .await?; - let operation_interceptor = DateGroupOperationInterceptorImpl {}; - let controller = - DateGroupController::new(&grouping_field, configuration, operation_interceptor).await?; + let configuration = + DateGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()).await?; + let controller = DateGroupController::new(&grouping_field, configuration, delegate).await?; group_controller = Box::new(controller); }, _ => { @@ -194,32 +146,21 @@ where let rows = row_details .iter() .map(|row| row.as_ref()) - .collect::>(); + .collect::>(); group_controller.fill_groups(rows.as_slice(), &grouping_field)?; Ok(group_controller) } #[tracing::instrument(level = "debug", skip_all)] -pub fn find_new_grouping_field(fields: &[Field], _layout: &DatabaseLayout) -> Option { - let mut groupable_field_revs = fields +pub fn find_suitable_grouping_field(fields: &[Field]) -> Option { + let groupable_field = fields .iter() - .flat_map(|field_rev| { - let field_type = FieldType::from(field_rev.field_type); - match field_type.can_be_group() { - true => Some(field_rev.clone()), - false => None, - } - }) - .collect::>(); + .find(|field| FieldType::from(field.field_type).can_be_group()); - if groupable_field_revs.is_empty() { - // If there is not groupable fields then we use the primary field. - fields - .iter() - .find(|field_rev| field_rev.is_primary) - .cloned() + if let Some(field) = groupable_field { + Some(field.clone()) } else { - Some(groupable_field_revs.remove(0)) + fields.iter().find(|field| field.is_primary).cloned() } } @@ -237,7 +178,6 @@ pub fn default_group_setting(field: &Field) -> GroupSetting { pub fn make_no_status_group(field: &Field) -> Group { Group { id: field.id.clone(), - name: format!("No {}", field.name), visible: true, } } diff --git a/frontend/rust-lib/flowy-database2/src/services/group/mod.rs b/frontend/rust-lib/flowy-database2/src/services/group/mod.rs index c9f9e91b65..c2ac8300b4 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/mod.rs @@ -5,6 +5,7 @@ mod controller_impls; mod entities; mod group_builder; +pub(crate) use action::GroupController; pub(crate) use configuration::*; pub(crate) use controller::*; pub(crate) use controller_impls::*; diff --git a/frontend/rust-lib/flowy-database2/tests/database/group_test/date_group_test.rs b/frontend/rust-lib/flowy-database2/tests/database/group_test/date_group_test.rs index 27215ec794..418dafa0f7 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/group_test/date_group_test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/group_test/date_group_test.rs @@ -63,56 +63,50 @@ async fn group_by_date_test() { row_count: 0, }, // Added via `make_test_board` - AssertGroupIDName { + AssertGroupId { group_index: 1, group_id: "2022/03/01".to_string(), - group_name: "Mar 2022".to_string(), }, AssertGroupRowCount { group_index: 1, row_count: 3, }, // Added via `make_test_board` - AssertGroupIDName { + AssertGroupId { group_index: 2, group_id: "2022/11/01".to_string(), - group_name: "Nov 2022".to_string(), }, AssertGroupRowCount { group_index: 2, row_count: 2, }, - AssertGroupIDName { + AssertGroupId { group_index: 3, group_id: last_30_days, - group_name: "Last 30 days".to_string(), }, AssertGroupRowCount { group_index: 3, row_count: 1, }, - AssertGroupIDName { + AssertGroupId { group_index: 4, group_id: last_day, - group_name: "Yesterday".to_string(), }, AssertGroupRowCount { group_index: 4, row_count: 2, }, - AssertGroupIDName { + AssertGroupId { group_index: 5, group_id: today.format("%Y/%m/%d").to_string(), - group_name: "Today".to_string(), }, AssertGroupRowCount { group_index: 5, row_count: 1, }, - AssertGroupIDName { + AssertGroupId { group_index: 6, group_id: next_7_days, - group_name: "Next 7 days".to_string(), }, AssertGroupRowCount { group_index: 6, @@ -180,10 +174,9 @@ async fn change_date_on_moving_row_to_another_group() { group_index: 2, row_count: 3, }, - AssertGroupIDName { + AssertGroupId { group_index: 2, group_id: "2022/11/01".to_string(), - group_name: "Nov 2022".to_string(), }, ]; test.run_scripts(scripts).await; diff --git a/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs b/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs index 6d1207cbdd..48f47b01e0 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/group_test/script.rs @@ -60,10 +60,9 @@ pub enum GroupScript { GroupByField { field_id: String, }, - AssertGroupIDName { + AssertGroupId { group_index: usize, group_id: String, - group_name: String, }, CreateGroup { name: String, @@ -241,7 +240,6 @@ impl DatabaseGroupTest { } => { let group = self.group_at_index(group_index).await; assert_eq!(group.group_id, group_pb.group_id); - assert_eq!(group.group_name, group_pb.group_name); }, GroupScript::UpdateSingleSelectSelectOption { inserted_options } => { self @@ -259,14 +257,12 @@ impl DatabaseGroupTest { .await .unwrap(); }, - GroupScript::AssertGroupIDName { + GroupScript::AssertGroupId { group_index, group_id, - group_name, } => { let group = self.group_at_index(group_index).await; assert_eq!(group_id, group.group_id, "group index: {}", group_index); - assert_eq!(group_name, group.group_name, "group index: {}", group_index); }, GroupScript::CreateGroup { name } => self .editor diff --git a/frontend/rust-lib/flowy-database2/tests/database/group_test/test.rs b/frontend/rust-lib/flowy-database2/tests/database/group_test/test.rs index 61e4d0e6aa..33e2b1563c 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/group_test/test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/group_test/test.rs @@ -457,13 +457,17 @@ async fn group_insert_single_select_option_test() { let scripts = vec![ AssertGroupCount(4), UpdateSingleSelectSelectOption { - inserted_options: vec![SelectOption::new(new_option_name)], + inserted_options: vec![SelectOption { + id: new_option_name.to_string(), + name: new_option_name.to_string(), + color: Default::default(), + }], }, AssertGroupCount(5), ]; test.run_scripts(scripts).await; let new_group = test.group_at_index(4).await; - assert_eq!(new_group.group_name, new_option_name); + assert_eq!(new_group.group_id, new_option_name); } #[tokio::test] @@ -499,6 +503,4 @@ async fn group_manual_create_new_group() { AssertGroupCount(5), ]; test.run_scripts(scripts).await; - let new_group = test.group_at_index(4).await; - assert_eq!(new_group.group_name, new_group_name); }