Forráskód Böngészése

refactor: group event (#2640)

* refactor: group event

* fix: tauri build
Nathan.fooo 2 éve
szülő
commit
6bbdc7ceff
39 módosított fájl, 583 hozzáadás és 485 törlés
  1. 2 2
      frontend/appflowy_flutter/lib/plugins/database_view/application/filter/filter_service.dart
  2. 4 4
      frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_listener.dart
  3. 37 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_service.dart
  4. 5 4
      frontend/appflowy_flutter/lib/plugins/database_view/application/setting/group_bloc.dart
  5. 0 17
      frontend/appflowy_flutter/lib/plugins/database_view/application/setting/setting_service.dart
  6. 4 4
      frontend/appflowy_flutter/lib/plugins/database_view/application/sort/sort_service.dart
  7. 11 12
      frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_observer.ts
  8. 6 6
      frontend/rust-lib/flowy-database2/src/entities/filter_entities/util.rs
  9. 68 55
      frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs
  10. 2 2
      frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs
  11. 10 28
      frontend/rust-lib/flowy-database2/src/entities/setting_entities.rs
  12. 5 5
      frontend/rust-lib/flowy-database2/src/entities/sort_entities.rs
  13. 36 13
      frontend/rust-lib/flowy-database2/src/event_handler.rs
  14. 15 7
      frontend/rust-lib/flowy-database2/src/event_map.rs
  15. 1 1
      frontend/rust-lib/flowy-database2/src/notification.rs
  16. 42 31
      frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs
  17. 3 3
      frontend/rust-lib/flowy-database2/src/services/database_view/notifier.rs
  18. 49 55
      frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs
  19. 0 2
      frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs
  20. 1 2
      frontend/rust-lib/flowy-database2/src/services/database_view/views.rs
  21. 2 0
      frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs
  22. 6 6
      frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs
  23. 8 6
      frontend/rust-lib/flowy-database2/src/services/filter/controller.rs
  24. 6 4
      frontend/rust-lib/flowy-database2/src/services/group/action.rs
  25. 41 51
      frontend/rust-lib/flowy-database2/src/services/group/configuration.rs
  26. 45 30
      frontend/rust-lib/flowy-database2/src/services/group/controller.rs
  27. 18 13
      frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs
  28. 18 5
      frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs
  29. 17 14
      frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
  30. 16 13
      frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
  31. 24 25
      frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs
  32. 12 2
      frontend/rust-lib/flowy-database2/src/services/group/entities.rs
  33. 1 1
      frontend/rust-lib/flowy-database2/src/services/group/group_util.rs
  34. 4 2
      frontend/rust-lib/flowy-database2/src/services/sort/controller.rs
  35. 12 12
      frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs
  36. 2 2
      frontend/rust-lib/flowy-database2/tests/database/filter_test/text_filter_test.rs
  37. 2 2
      frontend/rust-lib/flowy-database2/tests/database/sort_test/script.rs
  38. 34 20
      frontend/rust-lib/lib-log/src/layer.rs
  39. 14 24
      frontend/rust-lib/lib-log/src/lib.rs

+ 2 - 2
frontend/appflowy_flutter/lib/plugins/database_view/application/filter/filter_service.dart

@@ -178,7 +178,7 @@ class FilterBackendService {
     required FieldType fieldType,
     required List<int> data,
   }) {
-    var insertFilterPayload = AlterFilterPayloadPB.create()
+    var insertFilterPayload = UpdateFilterPayloadPB.create()
       ..fieldId = fieldId
       ..fieldType = fieldType
       ..viewId = viewId
@@ -190,7 +190,7 @@ class FilterBackendService {
 
     final payload = DatabaseSettingChangesetPB.create()
       ..viewId = viewId
-      ..alterFilter = insertFilterPayload;
+      ..updateFilter = insertFilterPayload;
     return DatabaseEventUpdateDatabaseSetting(payload).send().then((result) {
       return result.fold(
         (l) => left(l),

+ 4 - 4
frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_listener.dart

@@ -8,7 +8,7 @@ import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
 
-typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
+typedef GroupUpdateValue = Either<GroupChangesPB, FlowyError>;
 typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
 
 class DatabaseGroupListener {
@@ -36,17 +36,17 @@ class DatabaseGroupListener {
     Either<Uint8List, FlowyError> result,
   ) {
     switch (ty) {
-      case DatabaseNotification.DidUpdateGroups:
+      case DatabaseNotification.DidUpdateNumOfGroups:
         result.fold(
           (payload) => _numOfGroupsNotifier?.value =
-              left(GroupChangesetPB.fromBuffer(payload)),
+              left(GroupChangesPB.fromBuffer(payload)),
           (error) => _numOfGroupsNotifier?.value = right(error),
         );
         break;
       case DatabaseNotification.DidGroupByField:
         result.fold(
           (payload) => _groupByFieldNotifier?.value =
-              left(GroupChangesetPB.fromBuffer(payload).initialGroups),
+              left(GroupChangesPB.fromBuffer(payload).initialGroups),
           (error) => _groupByFieldNotifier?.value = right(error),
         );
         break;

+ 37 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_service.dart

@@ -0,0 +1,37 @@
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+
+class GroupBackendService {
+  final String viewId;
+  GroupBackendService(this.viewId);
+
+  Future<Either<Unit, FlowyError>> groupByField({
+    required String fieldId,
+    required FieldType fieldType,
+  }) {
+    final payload = GroupByFieldPayloadPB.create()
+      ..viewId = viewId
+      ..fieldId = fieldId
+      ..fieldType = fieldType;
+
+    return DatabaseEventSetGroupByField(payload).send();
+  }
+
+  Future<Either<Unit, FlowyError>> updateGroup({
+    required String groupId,
+    String? name,
+    bool? visible,
+  }) {
+    final payload = UpdateGroupPB.create()..groupId = groupId;
+    if (name != null) {
+      payload.name = name;
+    }
+    if (visible != null) {
+      payload.visible = visible;
+    }
+    return DatabaseEventUpdateGroup(payload).send();
+  }
+}

+ 5 - 4
frontend/appflowy_flutter/lib/plugins/database_view/application/setting/group_bloc.dart

@@ -1,23 +1,24 @@
 import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
-import 'package:appflowy/plugins/database_view/application/setting/setting_service.dart';
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 
+import '../group/group_service.dart';
+
 part 'group_bloc.freezed.dart';
 
 class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
   final FieldController _fieldController;
-  final SettingBackendService _settingBackendSvc;
+  final GroupBackendService _groupBackendSvc;
   Function(List<FieldInfo>)? _onFieldsFn;
 
   DatabaseGroupBloc({
     required String viewId,
     required FieldController fieldController,
   })  : _fieldController = fieldController,
-        _settingBackendSvc = SettingBackendService(viewId: viewId),
+        _groupBackendSvc = GroupBackendService(viewId),
         super(DatabaseGroupState.initial(viewId, fieldController.fieldInfos)) {
     on<DatabaseGroupEvent>(
       (event, emit) async {
@@ -29,7 +30,7 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
             emit(state.copyWith(fieldContexts: fieldContexts));
           },
           setGroupByField: (String fieldId, FieldType fieldType) async {
-            final result = await _settingBackendSvc.groupByField(
+            final result = await _groupBackendSvc.groupByField(
               fieldId: fieldId,
               fieldType: fieldType,
             );

+ 0 - 17
frontend/appflowy_flutter/lib/plugins/database_view/application/setting/setting_service.dart

@@ -2,8 +2,6 @@ import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.d
 import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/dispatch/dispatch.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
 
 class SettingBackendService {
@@ -15,19 +13,4 @@ class SettingBackendService {
     final payload = DatabaseViewIdPB.create()..value = viewId;
     return DatabaseEventGetDatabaseSetting(payload).send();
   }
-
-  Future<Either<Unit, FlowyError>> groupByField({
-    required String fieldId,
-    required FieldType fieldType,
-  }) {
-    final insertGroupPayload = InsertGroupPayloadPB.create()
-      ..viewId = viewId
-      ..fieldId = fieldId
-      ..fieldType = fieldType;
-    final payload = DatabaseSettingChangesetPB.create()
-      ..viewId = viewId
-      ..insertGroup = insertGroupPayload;
-
-    return DatabaseEventUpdateDatabaseSetting(payload).send();
-  }
 }

+ 4 - 4
frontend/appflowy_flutter/lib/plugins/database_view/application/sort/sort_service.dart

@@ -29,7 +29,7 @@ class SortBackendService {
     required FieldType fieldType,
     required SortConditionPB condition,
   }) {
-    var insertSortPayload = AlterSortPayloadPB.create()
+    var insertSortPayload = UpdateSortPayloadPB.create()
       ..fieldId = fieldId
       ..fieldType = fieldType
       ..viewId = viewId
@@ -38,7 +38,7 @@ class SortBackendService {
 
     final payload = DatabaseSettingChangesetPB.create()
       ..viewId = viewId
-      ..alterSort = insertSortPayload;
+      ..updateSort = insertSortPayload;
     return DatabaseEventUpdateDatabaseSetting(payload).send().then((result) {
       return result.fold(
         (l) => left(l),
@@ -55,7 +55,7 @@ class SortBackendService {
     required FieldType fieldType,
     required SortConditionPB condition,
   }) {
-    var insertSortPayload = AlterSortPayloadPB.create()
+    var insertSortPayload = UpdateSortPayloadPB.create()
       ..fieldId = fieldId
       ..fieldType = fieldType
       ..viewId = viewId
@@ -63,7 +63,7 @@ class SortBackendService {
 
     final payload = DatabaseSettingChangesetPB.create()
       ..viewId = viewId
-      ..alterSort = insertSortPayload;
+      ..updateSort = insertSortPayload;
     return DatabaseEventUpdateDatabaseSetting(payload).send().then((result) {
       return result.fold(
         (l) => left(l),

+ 11 - 12
frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_observer.ts

@@ -1,18 +1,17 @@
-import { ChangeNotifier } from "$app/utils/change_notifier";
-import { Ok, Result } from "ts-results";
-import { DatabaseNotification, FlowyError, GroupChangesetPB, GroupPB } from "@/services/backend";
-import { DatabaseNotificationObserver } from "../notifications/observer";
+import { ChangeNotifier } from '$app/utils/change_notifier';
+import { Ok, Result } from 'ts-results';
+import { DatabaseNotification, FlowyError, GroupChangesPB, GroupPB } from '@/services/backend';
+import { DatabaseNotificationObserver } from '../notifications/observer';
 
 export type GroupByFieldCallback = (value: Result<GroupPB[], FlowyError>) => void;
-export type GroupChangesetSubscribeCallback = (value: Result<GroupChangesetPB, FlowyError>) => void;
+export type GroupChangesetSubscribeCallback = (value: Result<GroupChangesPB, FlowyError>) => void;
 
 export class DatabaseGroupObserver {
   private groupByNotifier?: ChangeNotifier<Result<GroupPB[], FlowyError>>;
-  private groupChangesetNotifier?: ChangeNotifier<Result<GroupChangesetPB, FlowyError>>;
+  private groupChangesetNotifier?: ChangeNotifier<Result<GroupChangesPB, FlowyError>>;
   private listener?: DatabaseNotificationObserver;
 
-  constructor(public readonly viewId: string) {
-  }
+  constructor(public readonly viewId: string) {}
 
   subscribe = async (callbacks: {
     onGroupBy: GroupByFieldCallback;
@@ -30,14 +29,14 @@ export class DatabaseGroupObserver {
         switch (notification) {
           case DatabaseNotification.DidGroupByField:
             if (result.ok) {
-              this.groupByNotifier?.notify(Ok(GroupChangesetPB.deserializeBinary(result.val).initial_groups));
+              this.groupByNotifier?.notify(Ok(GroupChangesPB.deserializeBinary(result.val).initial_groups));
             } else {
               this.groupByNotifier?.notify(result);
             }
             break;
-          case DatabaseNotification.DidUpdateGroups:
+          case DatabaseNotification.DidUpdateNumOfGroups:
             if (result.ok) {
-              this.groupChangesetNotifier?.notify(Ok(GroupChangesetPB.deserializeBinary(result.val)));
+              this.groupChangesetNotifier?.notify(Ok(GroupChangesPB.deserializeBinary(result.val)));
             } else {
               this.groupChangesetNotifier?.notify(result);
             }
@@ -45,7 +44,7 @@ export class DatabaseGroupObserver {
           default:
             break;
         }
-      }
+      },
     });
 
     await this.listener.start();

+ 6 - 6
frontend/rust-lib/flowy-database2/src/entities/filter_entities/util.rs

@@ -125,7 +125,7 @@ pub struct DeleteFilterParams {
 }
 
 #[derive(ProtoBuf, Debug, Default, Clone)]
-pub struct AlterFilterPayloadPB {
+pub struct UpdateFilterPayloadPB {
   #[pb(index = 1)]
   pub field_id: String,
 
@@ -143,7 +143,7 @@ pub struct AlterFilterPayloadPB {
   pub view_id: String,
 }
 
-impl AlterFilterPayloadPB {
+impl UpdateFilterPayloadPB {
   #[allow(dead_code)]
   pub fn new<T: TryInto<Bytes, Error = ::protobuf::ProtobufError>>(
     view_id: &str,
@@ -162,10 +162,10 @@ impl AlterFilterPayloadPB {
   }
 }
 
-impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
+impl TryInto<UpdateFilterParams> for UpdateFilterPayloadPB {
   type Error = ErrorCode;
 
-  fn try_into(self) -> Result<AlterFilterParams, Self::Error> {
+  fn try_into(self) -> Result<UpdateFilterParams, Self::Error> {
     let view_id = NotEmptyStr::parse(self.view_id)
       .map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?
       .0;
@@ -217,7 +217,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
       },
     }
 
-    Ok(AlterFilterParams {
+    Ok(UpdateFilterParams {
       view_id,
       field_id,
       filter_id,
@@ -229,7 +229,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
 }
 
 #[derive(Debug)]
-pub struct AlterFilterParams {
+pub struct UpdateFilterParams {
   pub view_id: String,
   pub field_id: String,
   /// Create a new filter if the filter_id is None

+ 68 - 55
frontend/rust-lib/flowy-database2/src/entities/group_entities/group.rs

@@ -5,7 +5,7 @@ use flowy_error::ErrorCode;
 
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{FieldType, RowPB};
-use crate::services::group::{GroupData, GroupSetting};
+use crate::services::group::{GroupChangeset, GroupData, GroupSetting};
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GroupSettingPB {
@@ -25,6 +25,29 @@ impl std::convert::From<&GroupSetting> for GroupSettingPB {
   }
 }
 
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct RepeatedGroupSettingPB {
+  #[pb(index = 1)]
+  pub items: Vec<GroupSettingPB>,
+}
+
+impl std::convert::From<Vec<GroupSettingPB>> for RepeatedGroupSettingPB {
+  fn from(items: Vec<GroupSettingPB>) -> Self {
+    Self { items }
+  }
+}
+
+impl std::convert::From<Vec<GroupSetting>> for RepeatedGroupSettingPB {
+  fn from(group_settings: Vec<GroupSetting>) -> Self {
+    RepeatedGroupSettingPB {
+      items: group_settings
+        .iter()
+        .map(|setting| setting.into())
+        .collect(),
+    }
+  }
+}
+
 #[derive(ProtoBuf, Debug, Default, Clone)]
 pub struct RepeatedGroupPB {
   #[pb(index = 1)]
@@ -79,107 +102,97 @@ impl std::convert::From<GroupData> for GroupPB {
 }
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct RepeatedGroupSettingPB {
-  #[pb(index = 1)]
-  pub items: Vec<GroupSettingPB>,
-}
-
-impl std::convert::From<Vec<GroupSettingPB>> for RepeatedGroupSettingPB {
-  fn from(items: Vec<GroupSettingPB>) -> Self {
-    Self { items }
-  }
-}
-
-impl std::convert::From<Vec<GroupSetting>> for RepeatedGroupSettingPB {
-  fn from(group_settings: Vec<GroupSetting>) -> Self {
-    RepeatedGroupSettingPB {
-      items: group_settings
-        .iter()
-        .map(|setting| setting.into())
-        .collect(),
-    }
-  }
-}
-
-#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct InsertGroupPayloadPB {
+pub struct GroupByFieldPayloadPB {
   #[pb(index = 1)]
   pub field_id: String,
 
   #[pb(index = 2)]
-  pub field_type: FieldType,
+  pub view_id: String,
 
   #[pb(index = 3)]
-  pub view_id: String,
+  pub field_type: FieldType,
 }
 
-impl TryInto<InsertGroupParams> for InsertGroupPayloadPB {
+impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
   type Error = ErrorCode;
 
-  fn try_into(self) -> Result<InsertGroupParams, Self::Error> {
+  fn try_into(self) -> Result<GroupByFieldParams, Self::Error> {
     let field_id = NotEmptyStr::parse(self.field_id)
       .map_err(|_| ErrorCode::FieldIdIsEmpty)?
       .0;
-
     let view_id = NotEmptyStr::parse(self.view_id)
       .map_err(|_| ErrorCode::ViewIdIsInvalid)?
       .0;
 
-    Ok(InsertGroupParams {
+    Ok(GroupByFieldParams {
       field_id,
-      field_type: self.field_type,
       view_id,
+      field_type: self.field_type,
     })
   }
 }
 
-pub struct InsertGroupParams {
+pub struct GroupByFieldParams {
+  pub field_id: String,
+  pub view_id: String,
+  pub field_type: FieldType,
+}
+
+pub struct DeleteGroupParams {
   pub view_id: String,
   pub field_id: String,
+  pub group_id: String,
   pub field_type: FieldType,
 }
 
-#[derive(ProtoBuf, Debug, Default, Clone)]
-pub struct DeleteGroupPayloadPB {
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct UpdateGroupPB {
   #[pb(index = 1)]
-  pub field_id: String,
+  pub view_id: String,
 
   #[pb(index = 2)]
   pub group_id: String,
 
-  #[pb(index = 3)]
-  pub field_type: FieldType,
+  #[pb(index = 3, one_of)]
+  pub name: Option<String>,
 
-  #[pb(index = 4)]
-  pub view_id: String,
+  #[pb(index = 4, one_of)]
+  pub visible: Option<bool>,
 }
 
-impl TryInto<DeleteGroupParams> for DeleteGroupPayloadPB {
+impl TryInto<UpdateGroupParams> for UpdateGroupPB {
   type Error = ErrorCode;
 
-  fn try_into(self) -> Result<DeleteGroupParams, Self::Error> {
-    let field_id = NotEmptyStr::parse(self.field_id)
-      .map_err(|_| ErrorCode::FieldIdIsEmpty)?
-      .0;
-    let group_id = NotEmptyStr::parse(self.group_id)
-      .map_err(|_| ErrorCode::FieldIdIsEmpty)?
-      .0;
+  fn try_into(self) -> Result<UpdateGroupParams, Self::Error> {
     let view_id = NotEmptyStr::parse(self.view_id)
       .map_err(|_| ErrorCode::ViewIdIsInvalid)?
       .0;
+    let group_id = NotEmptyStr::parse(self.group_id)
+      .map_err(|_| ErrorCode::GroupIdIsEmpty)?
+      .0;
 
-    Ok(DeleteGroupParams {
-      field_id,
-      field_type: self.field_type,
-      group_id,
+    Ok(UpdateGroupParams {
       view_id,
+      group_id,
+      name: self.name,
+      visible: self.visible,
     })
   }
 }
 
-pub struct DeleteGroupParams {
+pub struct UpdateGroupParams {
   pub view_id: String,
-  pub field_id: String,
   pub group_id: String,
-  pub field_type: FieldType,
+  pub name: Option<String>,
+  pub visible: Option<bool>,
+}
+
+impl From<UpdateGroupParams> for GroupChangeset {
+  fn from(params: UpdateGroupParams) -> Self {
+    Self {
+      group_id: params.group_id,
+      name: params.name,
+      visible: params.visible,
+    }
+  }
 }

+ 2 - 2
frontend/rust-lib/flowy-database2/src/entities/group_entities/group_changeset.rs

@@ -129,7 +129,7 @@ impl TryInto<MoveGroupParams> for MoveGroupPayloadPB {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct GroupChangesetPB {
+pub struct GroupChangesPB {
   #[pb(index = 1)]
   pub view_id: String,
 
@@ -146,7 +146,7 @@ pub struct GroupChangesetPB {
   pub update_groups: Vec<GroupPB>,
 }
 
-impl GroupChangesetPB {
+impl GroupChangesPB {
   pub fn is_empty(&self) -> bool {
     self.initial_groups.is_empty()
       && self.inserted_groups.is_empty()

+ 10 - 28
frontend/rust-lib/flowy-database2/src/entities/setting_entities.rs

@@ -8,10 +8,9 @@ use flowy_error::ErrorCode;
 
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{
-  AlterFilterParams, AlterFilterPayloadPB, AlterSortParams, AlterSortPayloadPB,
-  CalendarLayoutSettingPB, DeleteFilterParams, DeleteFilterPayloadPB, DeleteGroupParams,
-  DeleteGroupPayloadPB, DeleteSortParams, DeleteSortPayloadPB, InsertGroupParams,
-  InsertGroupPayloadPB, RepeatedFilterPB, RepeatedGroupSettingPB, RepeatedSortPB,
+  CalendarLayoutSettingPB, DeleteFilterParams, DeleteFilterPayloadPB, DeleteSortParams,
+  DeleteSortPayloadPB, RepeatedFilterPB, RepeatedGroupSettingPB, RepeatedSortPB,
+  UpdateFilterParams, UpdateFilterPayloadPB, UpdateGroupPB, UpdateSortParams, UpdateSortPayloadPB,
 };
 use crate::services::setting::CalendarLayoutSetting;
 
@@ -77,21 +76,18 @@ pub struct DatabaseSettingChangesetPB {
   pub layout_type: DatabaseLayoutPB,
 
   #[pb(index = 3, one_of)]
-  pub alter_filter: Option<AlterFilterPayloadPB>,
+  pub update_filter: Option<UpdateFilterPayloadPB>,
 
   #[pb(index = 4, one_of)]
   pub delete_filter: Option<DeleteFilterPayloadPB>,
 
   #[pb(index = 5, one_of)]
-  pub insert_group: Option<InsertGroupPayloadPB>,
+  pub update_group: Option<UpdateGroupPB>,
 
   #[pb(index = 6, one_of)]
-  pub delete_group: Option<DeleteGroupPayloadPB>,
+  pub update_sort: Option<UpdateSortPayloadPB>,
 
   #[pb(index = 7, one_of)]
-  pub alter_sort: Option<AlterSortPayloadPB>,
-
-  #[pb(index = 8, one_of)]
   pub delete_sort: Option<DeleteSortPayloadPB>,
 }
 
@@ -103,7 +99,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
       .map_err(|_| ErrorCode::ViewIdIsInvalid)?
       .0;
 
-    let insert_filter = match self.alter_filter {
+    let insert_filter = match self.update_filter {
       None => None,
       Some(payload) => Some(payload.try_into()?),
     };
@@ -113,17 +109,7 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
       Some(payload) => Some(payload.try_into()?),
     };
 
-    let insert_group = match self.insert_group {
-      Some(payload) => Some(payload.try_into()?),
-      None => None,
-    };
-
-    let delete_group = match self.delete_group {
-      Some(payload) => Some(payload.try_into()?),
-      None => None,
-    };
-
-    let alert_sort = match self.alter_sort {
+    let alert_sort = match self.update_sort {
       None => None,
       Some(payload) => Some(payload.try_into()?),
     };
@@ -138,8 +124,6 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
       layout_type: self.layout_type.into(),
       insert_filter,
       delete_filter,
-      insert_group,
-      delete_group,
       alert_sort,
       delete_sort,
     })
@@ -149,11 +133,9 @@ impl TryInto<DatabaseSettingChangesetParams> for DatabaseSettingChangesetPB {
 pub struct DatabaseSettingChangesetParams {
   pub view_id: String,
   pub layout_type: DatabaseLayout,
-  pub insert_filter: Option<AlterFilterParams>,
+  pub insert_filter: Option<UpdateFilterParams>,
   pub delete_filter: Option<DeleteFilterParams>,
-  pub insert_group: Option<InsertGroupParams>,
-  pub delete_group: Option<DeleteGroupParams>,
-  pub alert_sort: Option<AlterSortParams>,
+  pub alert_sort: Option<UpdateSortParams>,
   pub delete_sort: Option<DeleteSortParams>,
 }
 

+ 5 - 5
frontend/rust-lib/flowy-database2/src/entities/sort_entities.rs

@@ -92,7 +92,7 @@ impl std::convert::From<SortConditionPB> for SortCondition {
 }
 
 #[derive(ProtoBuf, Debug, Default, Clone)]
-pub struct AlterSortPayloadPB {
+pub struct UpdateSortPayloadPB {
   #[pb(index = 1)]
   pub view_id: String,
 
@@ -110,10 +110,10 @@ pub struct AlterSortPayloadPB {
   pub condition: SortConditionPB,
 }
 
-impl TryInto<AlterSortParams> for AlterSortPayloadPB {
+impl TryInto<UpdateSortParams> for UpdateSortPayloadPB {
   type Error = ErrorCode;
 
-  fn try_into(self) -> Result<AlterSortParams, Self::Error> {
+  fn try_into(self) -> Result<UpdateSortParams, Self::Error> {
     let view_id = NotEmptyStr::parse(self.view_id)
       .map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?
       .0;
@@ -131,7 +131,7 @@ impl TryInto<AlterSortParams> for AlterSortPayloadPB {
       ),
     };
 
-    Ok(AlterSortParams {
+    Ok(UpdateSortParams {
       view_id,
       field_id,
       sort_id,
@@ -142,7 +142,7 @@ impl TryInto<AlterSortParams> for AlterSortPayloadPB {
 }
 
 #[derive(Debug)]
-pub struct AlterSortParams {
+pub struct UpdateSortParams {
   pub view_id: String,
   pub field_id: String,
   /// Create a new sort if the sort is None

+ 36 - 13
frontend/rust-lib/flowy-database2/src/event_handler.rs

@@ -15,9 +15,10 @@ use crate::services::cell::CellBuilder;
 use crate::services::field::{
   type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset,
 };
+use crate::services::group::{GroupChangeset, GroupSettingChangeset};
 use crate::services::share::csv::CSVFormat;
 
-#[tracing::instrument(level = "trace", skip(data, manager), err)]
+#[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn get_database_data_handler(
   data: AFPluginData<DatabaseViewIdPB>,
   manager: AFPluginState<Arc<DatabaseManager2>>,
@@ -49,24 +50,16 @@ pub(crate) async fn update_database_setting_handler(
   let params: DatabaseSettingChangesetParams = data.into_inner().try_into()?;
   let editor = manager.get_database_with_view_id(&params.view_id).await?;
 
-  if let Some(insert_params) = params.insert_group {
-    editor.insert_group(insert_params).await?;
-  }
-
-  if let Some(delete_params) = params.delete_group {
-    editor.delete_group(delete_params).await?;
-  }
-
-  if let Some(alter_filter) = params.insert_filter {
-    editor.create_or_update_filter(alter_filter).await?;
+  if let Some(update_filter) = params.insert_filter {
+    editor.create_or_update_filter(update_filter).await?;
   }
 
   if let Some(delete_filter) = params.delete_filter {
     editor.delete_filter(delete_filter).await?;
   }
 
-  if let Some(alter_sort) = params.alert_sort {
-    let _ = editor.create_or_update_sort(alter_sort).await?;
+  if let Some(update_sort) = params.alert_sort {
+    let _ = editor.create_or_update_sort(update_sort).await?;
   }
   if let Some(delete_sort) = params.delete_sort {
     editor.delete_sort(delete_sort).await?;
@@ -525,6 +518,36 @@ pub(crate) async fn get_group_handler(
   data_result_ok(group)
 }
 
+#[tracing::instrument(level = "trace", skip_all, err)]
+pub(crate) async fn set_group_by_field_handler(
+  data: AFPluginData<GroupByFieldPayloadPB>,
+  manager: AFPluginState<Arc<DatabaseManager2>>,
+) -> FlowyResult<()> {
+  let params: GroupByFieldParams = data.into_inner().try_into()?;
+  let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
+  database_editor
+    .set_group_by_field(&params.view_id, &params.field_id)
+    .await?;
+  Ok(())
+}
+
+#[tracing::instrument(level = "trace", skip_all, err)]
+pub(crate) async fn update_group_handler(
+  data: AFPluginData<UpdateGroupPB>,
+  manager: AFPluginState<Arc<DatabaseManager2>>,
+) -> FlowyResult<()> {
+  let params: UpdateGroupParams = data.into_inner().try_into()?;
+  let view_id = params.view_id.clone();
+  let database_editor = manager.get_database_with_view_id(&view_id).await?;
+  let group_setting_changeset = GroupSettingChangeset {
+    update_groups: vec![GroupChangeset::from(params)],
+  };
+  database_editor
+    .update_group_setting(&view_id, group_setting_changeset)
+    .await?;
+  Ok(())
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn move_group_handler(
   data: AFPluginData<MoveGroupPayloadPB>,

+ 15 - 7
frontend/rust-lib/flowy-database2/src/event_map.rs

@@ -51,6 +51,8 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
         .event(DatabaseEvent::MoveGroupRow, move_group_row_handler)
         .event(DatabaseEvent::GetGroups, get_groups_handler)
         .event(DatabaseEvent::GetGroup, get_group_handler)
+        .event(DatabaseEvent::SetGroupByField, set_group_by_field_handler)
+        .event(DatabaseEvent::UpdateGroup, update_group_handler)
         // Database
         .event(DatabaseEvent::GetDatabases, get_databases_handler)
         // Calendar
@@ -243,25 +245,31 @@ pub enum DatabaseEvent {
   #[event(input = "MoveGroupRowPayloadPB")]
   MoveGroupRow = 112,
 
+  #[event(input = "GroupByFieldPayloadPB")]
+  SetGroupByField = 113,
+
+  #[event(input = "UpdateGroupPB")]
+  UpdateGroup = 114,
+
   /// Returns all the databases
   #[event(output = "RepeatedDatabaseDescriptionPB")]
-  GetDatabases = 114,
+  GetDatabases = 120,
 
   #[event(input = "LayoutSettingChangesetPB")]
-  SetLayoutSetting = 115,
+  SetLayoutSetting = 121,
 
   #[event(input = "DatabaseLayoutIdPB", output = "LayoutSettingPB")]
-  GetLayoutSetting = 116,
+  GetLayoutSetting = 122,
 
   #[event(input = "CalendarEventRequestPB", output = "RepeatedCalendarEventPB")]
-  GetAllCalendarEvents = 117,
+  GetAllCalendarEvents = 123,
 
   #[event(input = "RowIdPB", output = "CalendarEventPB")]
-  GetCalendarEvent = 118,
+  GetCalendarEvent = 124,
 
   #[event(input = "MoveCalendarEventPB")]
-  MoveCalendarEvent = 119,
+  MoveCalendarEvent = 125,
 
   #[event(input = "DatabaseImportPB")]
-  ImportCSV = 120,
+  ImportCSV = 130,
 }

+ 1 - 1
frontend/rust-lib/flowy-database2/src/notification.rs

@@ -16,7 +16,7 @@ pub enum DatabaseNotification {
   /// Trigger after editing a field properties including rename,update type option, etc
   DidUpdateField = 50,
   /// Trigger after the number of groups is changed
-  DidUpdateGroups = 60,
+  DidUpdateNumOfGroups = 60,
   /// Trigger after inserting/deleting/updating/moving a row
   DidUpdateGroupRow = 61,
   /// Trigger when setting a new grouping field

+ 42 - 31
frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs

@@ -15,11 +15,11 @@ use flowy_task::TaskDispatcher;
 use lib_infra::future::{to_fut, Fut};
 
 use crate::entities::{
-  AlterFilterParams, AlterSortParams, CalendarEventPB, CellChangesetNotifyPB, CellPB,
-  DatabaseFieldChangesetPB, DatabasePB, DatabaseViewSettingPB, DeleteFilterParams,
-  DeleteGroupParams, DeleteSortParams, FieldChangesetParams, FieldIdPB, FieldPB, FieldType,
-  GroupPB, IndexFieldPB, InsertGroupParams, InsertedRowPB, LayoutSettingParams, RepeatedFilterPB,
-  RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB, SelectOptionCellDataPB, SelectOptionPB,
+  CalendarEventPB, CellChangesetNotifyPB, CellPB, DatabaseFieldChangesetPB, DatabasePB,
+  DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
+  FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
+  LayoutSettingParams, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB, RowPB, RowsChangePB,
+  SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams, UpdateSortParams,
 };
 use crate::notification::{send_notification, DatabaseNotification};
 use crate::services::cell::{
@@ -34,7 +34,9 @@ use crate::services::field::{
   SelectOptionIds, TypeOptionCellDataHandler, TypeOptionCellExt,
 };
 use crate::services::filter::Filter;
-use crate::services::group::{default_group_setting, GroupSetting, RowChangeset};
+use crate::services::group::{
+  default_group_setting, GroupSetting, GroupSettingChangeset, RowChangeset,
+};
 use crate::services::share::csv::{CSVExport, CSVFormat};
 use crate::services::sort::Sort;
 
@@ -85,18 +87,18 @@ impl DatabaseEditor {
     self.database.lock().fields.get_field(field_id)
   }
 
-  pub async fn insert_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
+  pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
     {
       let database = self.database.lock();
-      let field = database.fields.get_field(&params.field_id);
+      let field = database.fields.get_field(field_id);
       if let Some(field) = field {
         let group_setting = default_group_setting(&field);
-        database.insert_group_setting(&params.view_id, group_setting);
+        database.insert_group_setting(view_id, group_setting);
       }
     }
 
-    let view_editor = self.database_views.get_view_editor(&params.view_id).await?;
-    view_editor.v_initialize_new_group(params).await?;
+    let view_editor = self.database_views.get_view_editor(view_id).await?;
+    view_editor.v_initialize_new_group(field_id).await?;
     Ok(())
   }
 
@@ -111,8 +113,20 @@ impl DatabaseEditor {
     Ok(())
   }
 
+  pub async fn update_group_setting(
+    &self,
+    view_id: &str,
+    group_setting_changeset: GroupSettingChangeset,
+  ) -> FlowyResult<()> {
+    let view_editor = self.database_views.get_view_editor(view_id).await?;
+    view_editor
+      .update_group_setting(group_setting_changeset)
+      .await?;
+    Ok(())
+  }
+
   #[tracing::instrument(level = "trace", skip_all, err)]
-  pub async fn create_or_update_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
+  pub async fn create_or_update_filter(&self, params: UpdateFilterParams) -> FlowyResult<()> {
     let view_editor = self.database_views.get_view_editor(&params.view_id).await?;
     view_editor.v_insert_filter(params).await?;
     Ok(())
@@ -124,7 +138,7 @@ impl DatabaseEditor {
     Ok(())
   }
 
-  pub async fn create_or_update_sort(&self, params: AlterSortParams) -> FlowyResult<Sort> {
+  pub async fn create_or_update_sort(&self, params: UpdateSortParams) -> FlowyResult<Sort> {
     let view_editor = self.database_views.get_view_editor(&params.view_id).await?;
     let sort = view_editor.v_insert_sort(params).await?;
     Ok(sort)
@@ -549,6 +563,8 @@ impl DatabaseEditor {
     Some(SelectOptionPB::from(select_option))
   }
 
+  /// Insert the options into the field's type option and update the cell content with the new options.
+  /// Only used for single select and multiple select.
   pub async fn insert_select_options(
     &self,
     view_id: &str,
@@ -556,30 +572,25 @@ impl DatabaseEditor {
     row_id: RowId,
     options: Vec<SelectOptionPB>,
   ) -> FlowyResult<()> {
-    let field = match self.database.lock().fields.get_field(field_id) {
-      Some(field) => Ok(field),
-      None => {
-        let msg = format!("Field with id:{} not found", &field_id);
-        Err(FlowyError::internal().context(msg))
-      },
-    }?;
+    let field = self.database.lock().fields.get_field(field_id).ok_or(
+      FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id)),
+    )?;
+    debug_assert!(FieldType::from(field.field_type).is_select_option());
+
     let mut type_option = select_type_option_from_field(&field)?;
     let cell_changeset = SelectOptionCellChangeset {
       insert_option_ids: options.iter().map(|option| option.id.clone()).collect(),
       ..Default::default()
     };
+    options
+      .into_iter()
+      .for_each(|option| type_option.insert_option(option.into()));
 
-    for option in options {
-      type_option.insert_option(option.into());
-    }
+    // Update the field's type option
     self
-      .database
-      .lock()
-      .fields
-      .update_field(field_id, |update| {
-        update.set_type_option(field.field_type, Some(type_option.to_type_option_data()));
-      });
-
+      .update_field_type_option(view_id, field_id, type_option.to_type_option_data(), field)
+      .await?;
+    // Insert the options into the cell
     self
       .update_cell_with_changeset(view_id, row_id, field_id, cell_changeset)
       .await?;
@@ -709,7 +720,7 @@ impl DatabaseEditor {
 
   pub async fn group_by_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
     let view = self.database_views.get_view_editor(view_id).await?;
-    view.v_update_group_setting(field_id).await?;
+    view.v_update_grouping_field(field_id).await?;
     Ok(())
   }
 

+ 3 - 3
frontend/rust-lib/flowy-database2/src/services/database_view/notifier.rs

@@ -1,6 +1,6 @@
 #![allow(clippy::while_let_loop)]
 use crate::entities::{
-  DatabaseViewSettingPB, FilterChangesetNotificationPB, GroupChangesetPB, GroupRowsNotificationPB,
+  DatabaseViewSettingPB, FilterChangesetNotificationPB, GroupChangesPB, GroupRowsNotificationPB,
   ReorderAllRowsPB, ReorderSingleRowPB, RowsVisibilityChangePB, SortChangesetNotificationPB,
 };
 use crate::notification::{send_notification, DatabaseNotification};
@@ -102,8 +102,8 @@ pub async fn notify_did_update_sort(notification: SortChangesetNotificationPB) {
   }
 }
 
-pub(crate) async fn notify_did_update_groups(view_id: &str, changeset: GroupChangesetPB) {
-  send_notification(view_id, DatabaseNotification::DidUpdateGroups)
+pub(crate) async fn notify_did_update_num_of_groups(view_id: &str, changeset: GroupChangesPB) {
+  send_notification(view_id, DatabaseNotification::DidUpdateNumOfGroups)
     .payload(changeset)
     .send();
 }

+ 49 - 55
frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs

@@ -13,10 +13,10 @@ use flowy_task::TaskDispatcher;
 use lib_infra::future::Fut;
 
 use crate::entities::{
-  AlterFilterParams, AlterSortParams, CalendarEventPB, DeleteFilterParams, DeleteGroupParams,
-  DeleteSortParams, FieldType, GroupChangesetPB, GroupPB, GroupRowsNotificationPB,
-  InsertGroupParams, InsertedGroupPB, InsertedRowPB, LayoutSettingPB, LayoutSettingParams, RowPB,
-  RowsChangePB, SortChangesetNotificationPB, SortPB,
+  CalendarEventPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams, FieldType,
+  GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedRowPB, LayoutSettingPB,
+  LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
+  UpdateFilterParams, UpdateSortParams,
 };
 use crate::notification::{send_notification, DatabaseNotification};
 use crate::services::cell::CellCache;
@@ -27,7 +27,7 @@ use crate::services::database_view::view_group::{
 };
 use crate::services::database_view::view_sort::make_sort_controller;
 use crate::services::database_view::{
-  notify_did_update_filter, notify_did_update_group_rows, notify_did_update_groups,
+  notify_did_update_filter, notify_did_update_group_rows, notify_did_update_num_of_groups,
   notify_did_update_setting, notify_did_update_sort, DatabaseViewChangedNotifier,
   DatabaseViewChangedReceiverRunner,
 };
@@ -35,7 +35,9 @@ use crate::services::field::TypeOptionCellDataHandler;
 use crate::services::filter::{
   Filter, FilterChangeset, FilterController, FilterType, UpdatedFilterType,
 };
-use crate::services::group::{GroupController, GroupSetting, MoveGroupRowContext, RowChangeset};
+use crate::services::group::{
+  GroupController, GroupSetting, GroupSettingChangeset, MoveGroupRowContext, RowChangeset,
+};
 use crate::services::setting::CalendarLayoutSetting;
 use crate::services::sort::{DeletedSortType, Sort, SortChangeset, SortController, SortType};
 
@@ -237,26 +239,28 @@ impl DatabaseViewEditor {
       .await;
 
     if let Some(Ok(result)) = result {
-      let mut changeset = GroupChangesetPB {
+      let mut group_changes = GroupChangesPB {
         view_id: self.view_id.clone(),
         ..Default::default()
       };
       if let Some(inserted_group) = result.inserted_group {
         tracing::trace!("Create group after editing the row: {:?}", inserted_group);
-        changeset.inserted_groups.push(inserted_group);
+        group_changes.inserted_groups.push(inserted_group);
       }
       if let Some(delete_group) = result.deleted_group {
         tracing::trace!("Delete group after editing the row: {:?}", delete_group);
-        changeset.deleted_groups.push(delete_group.group_id);
+        group_changes.deleted_groups.push(delete_group.group_id);
+      }
+
+      if !group_changes.is_empty() {
+        notify_did_update_num_of_groups(&self.view_id, group_changes).await;
       }
-      notify_did_update_groups(&self.view_id, changeset).await;
 
-      tracing::trace!(
-        "Group changesets after editing the row: {:?}",
-        result.row_changesets
-      );
       for changeset in result.row_changesets {
-        notify_did_update_group_rows(changeset).await;
+        if !changeset.is_empty() {
+          tracing::trace!("Group change after editing the row: {:?}", changeset);
+          notify_did_update_group_rows(changeset).await;
+        }
       }
     } else {
       let update_row = UpdatedRow {
@@ -326,15 +330,15 @@ impl DatabaseViewEditor {
       .await;
 
     if let Some(result) = result {
-      let mut changeset = GroupChangesetPB {
-        view_id: self.view_id.clone(),
-        ..Default::default()
-      };
       if let Some(delete_group) = result.deleted_group {
-        tracing::info!("Delete group after moving the row: {:?}", delete_group);
-        changeset.deleted_groups.push(delete_group.group_id);
+        tracing::trace!("Delete group after moving the row: {:?}", delete_group);
+        let mut changes = GroupChangesPB {
+          view_id: self.view_id.clone(),
+          ..Default::default()
+        };
+        changes.deleted_groups.push(delete_group.group_id);
+        notify_did_update_num_of_groups(&self.view_id, changes).await;
       }
-      notify_did_update_groups(&self.view_id, changeset).await;
 
       for changeset in result.row_changesets {
         notify_did_update_group_rows(changeset).await;
@@ -371,25 +375,6 @@ impl DatabaseViewEditor {
       .write()
       .await
       .move_group(from_group, to_group)?;
-    match self.group_controller.read().await.get_group(from_group) {
-      None => tracing::warn!("Can not find the group with id: {}", from_group),
-      Some((index, group)) => {
-        let inserted_group = InsertedGroupPB {
-          group: GroupPB::from(group),
-          index: index as i32,
-        };
-
-        let changeset = GroupChangesetPB {
-          view_id: self.view_id.clone(),
-          inserted_groups: vec![inserted_group],
-          deleted_groups: vec![from_group.to_string()],
-          update_groups: vec![],
-          initial_groups: vec![],
-        };
-
-        notify_did_update_groups(&self.view_id, changeset).await;
-      },
-    }
     Ok(())
   }
 
@@ -397,9 +382,9 @@ impl DatabaseViewEditor {
     self.group_controller.read().await.field_id().to_string()
   }
 
-  pub async fn v_initialize_new_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
-    if self.group_controller.read().await.field_id() != params.field_id {
-      self.v_update_group_setting(&params.field_id).await?;
+  pub async fn v_initialize_new_group(&self, field_id: &str) -> FlowyResult<()> {
+    if self.group_controller.read().await.field_id() != field_id {
+      self.v_update_grouping_field(field_id).await?;
 
       if let Some(view) = self.delegate.get_view_setting(&self.view_id).await {
         let setting = database_view_setting_pb_from_view(view);
@@ -413,12 +398,20 @@ impl DatabaseViewEditor {
     Ok(())
   }
 
+  pub async fn update_group_setting(&self, changeset: GroupSettingChangeset) -> FlowyResult<()> {
+    self
+      .group_controller
+      .write()
+      .await
+      .apply_group_setting_changeset(changeset)
+  }
+
   pub async fn v_get_all_sorts(&self) -> Vec<Sort> {
     self.delegate.get_all_sorts(&self.view_id)
   }
 
   #[tracing::instrument(level = "trace", skip(self), err)]
-  pub async fn v_insert_sort(&self, params: AlterSortParams) -> FlowyResult<Sort> {
+  pub async fn v_insert_sort(&self, params: UpdateSortParams) -> FlowyResult<Sort> {
     let is_exist = params.sort_id.is_some();
     let sort_id = match params.sort_id {
       None => gen_database_sort_id(),
@@ -479,7 +472,7 @@ impl DatabaseViewEditor {
   }
 
   #[tracing::instrument(level = "trace", skip(self), err)]
-  pub async fn v_insert_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
+  pub async fn v_insert_filter(&self, params: UpdateFilterParams) -> FlowyResult<()> {
     let is_exist = params.filter_id.is_some();
     let filter_id = match params.filter_id {
       None => gen_database_filter_id(),
@@ -634,9 +627,15 @@ impl DatabaseViewEditor {
         .sort_controller
         .read()
         .await
-        .did_update_view_field_type_option(&field)
+        .did_update_field_type_option(&field)
         .await;
 
+      self
+        .group_controller
+        .write()
+        .await
+        .did_update_field_type_option(&field);
+
       if let Some(filter) = self
         .delegate
         .get_filter_by_field_id(&self.view_id, field_id)
@@ -660,14 +659,9 @@ impl DatabaseViewEditor {
     Ok(())
   }
 
-  ///
-  ///
-  /// # Arguments
-  ///
-  /// * `field_id`:
-  ///
+  /// Called when a grouping field is updated.
   #[tracing::instrument(level = "debug", skip_all, err)]
-  pub async fn v_update_group_setting(&self, field_id: &str) -> FlowyResult<()> {
+  pub async fn v_update_grouping_field(&self, field_id: &str) -> FlowyResult<()> {
     if let Some(field) = self.delegate.get_field(field_id).await {
       let new_group_controller =
         new_group_controller_with_field(self.view_id.clone(), self.delegate.clone(), field).await?;
@@ -679,7 +673,7 @@ impl DatabaseViewEditor {
         .collect();
 
       *self.group_controller.write().await = new_group_controller;
-      let changeset = GroupChangesetPB {
+      let changeset = GroupChangesPB {
         view_id: self.view_id.clone(),
         initial_groups: new_groups,
         ..Default::default()

+ 0 - 2
frontend/rust-lib/flowy-database2/src/services/database_view/view_group.rs

@@ -5,7 +5,6 @@ use collab_database::rows::RowId;
 
 use flowy_error::{FlowyError, FlowyResult};
 use lib_infra::future::{to_fut, Fut};
-use tracing::trace;
 
 use crate::entities::FieldType;
 use crate::services::database_view::DatabaseViewData;
@@ -43,7 +42,6 @@ pub async fn new_group_controller(
   let fields = delegate.get_fields(&view_id, None).await;
   let rows = delegate.get_rows(&view_id).await;
   let layout = delegate.get_layout_for_view(&view_id);
-  trace!(?fields, ?rows, ?layout, "new_group_controller");
 
   // Read the grouping field or find a new grouping field
   let mut grouping_field = setting_reader

+ 1 - 2
frontend/rust-lib/flowy-database2/src/services/database_view/views.rs

@@ -94,7 +94,7 @@ impl DatabaseViews {
     // If the id of the grouping field is equal to the updated field's id, then we need to
     // update the group setting
     if view_editor.group_id().await == field_id {
-      view_editor.v_update_group_setting(field_id).await?;
+      view_editor.v_update_grouping_field(field_id).await?;
     }
     view_editor
       .v_did_update_field_type_option(field_id, old_field)
@@ -108,7 +108,6 @@ impl DatabaseViews {
       return Ok(editor.clone());
     }
 
-    tracing::trace!("{:p} create view:{} editor", self, view_id);
     let mut editor_map = self.editor_map.write().await;
     let editor = Arc::new(
       DatabaseViewEditor::new(

+ 2 - 0
frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs

@@ -22,6 +22,8 @@ pub trait SelectTypeOptionSharedAction: Send + Sync {
   fn number_of_max_options(&self) -> Option<usize>;
 
   /// Insert the `SelectOption` into corresponding type option.
+  /// If the option already exists, it will be updated.
+  /// If the option does not exist, it will be inserted at the beginning.
   fn insert_option(&mut self, new_option: SelectOption) {
     let options = self.mut_options();
     if let Some(index) = options

+ 6 - 6
frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs

@@ -131,12 +131,12 @@ where
     if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
       let read_guard = cell_data_cache.read();
       if let Some(cell_data) = read_guard.get(key.as_ref()).cloned() {
-        tracing::trace!(
-          "Cell cache hit: field_type:{}, cell: {:?}, cell_data: {:?}",
-          decoded_field_type,
-          cell,
-          cell_data
-        );
+        // tracing::trace!(
+        //   "Cell cache hit: field_type:{}, cell: {:?}, cell_data: {:?}",
+        //   decoded_field_type,
+        //   cell,
+        //   cell_data
+        // );
         return Ok(cell_data);
       }
     }

+ 8 - 6
frontend/rust-lib/flowy-database2/src/services/filter/controller.rs

@@ -219,12 +219,14 @@ impl FilterController {
   }
 
   pub async fn did_receive_row_changed(&self, row_id: RowId) {
-    self
-      .gen_task(
-        FilterEvent::RowDidChanged(row_id),
-        QualityOfService::UserInteractive,
-      )
-      .await
+    if !self.cell_filter_cache.read().is_empty() {
+      self
+        .gen_task(
+          FilterEvent::RowDidChanged(row_id),
+          QualityOfService::UserInteractive,
+        )
+        .await
+    }
   }
 
   #[tracing::instrument(level = "trace", skip(self))]

+ 6 - 4
frontend/rust-lib/flowy-database2/src/services/group/action.rs

@@ -1,7 +1,7 @@
-use crate::entities::{GroupChangesetPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
+use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
 use crate::services::cell::DecodedCellData;
 use crate::services::group::controller::MoveGroupRowContext;
-use crate::services::group::GroupData;
+use crate::services::group::{GroupData, GroupSettingChangeset};
 use collab_database::fields::Field;
 use collab_database::rows::{Cell, Row};
 
@@ -65,7 +65,7 @@ pub trait GroupCustomize: Send + Sync {
 }
 
 /// Defines the shared actions any group controller can perform.
-pub trait GroupControllerActions: Send + Sync {
+pub trait GroupControllerOperation: Send + Sync {
   /// The field that is used for grouping the rows
   fn field_id(&self) -> &str;
 
@@ -100,7 +100,9 @@ pub trait GroupControllerActions: Send + Sync {
   fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult>;
 
   /// Update the group if the corresponding field is changed
-  fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesetPB>>;
+  fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesPB>>;
+
+  fn apply_group_setting_changeset(&mut self, changeset: GroupSettingChangeset) -> FlowyResult<()>;
 }
 
 #[derive(Debug)]

+ 41 - 51
frontend/rust-lib/flowy-database2/src/services/group/configuration.rs

@@ -1,7 +1,7 @@
-use crate::entities::{GroupChangesetPB, GroupPB, InsertedGroupPB};
+use crate::entities::{GroupChangesPB, GroupPB, InsertedGroupPB};
 use crate::services::field::RowSingleCellData;
 use crate::services::group::{
-  default_group_setting, GeneratedGroupContext, Group, GroupData, GroupSetting,
+  default_group_setting, GeneratedGroups, Group, GroupChangeset, GroupData, GroupSetting,
 };
 use collab_database::fields::Field;
 use flowy_error::{FlowyError, FlowyResult};
@@ -25,7 +25,7 @@ pub trait GroupSettingWriter: Send + Sync + 'static {
 
 impl<T> std::fmt::Display for GroupContext<T> {
   fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-    self.groups_map.iter().for_each(|(_, group)| {
+    self.group_by_id.iter().for_each(|(_, group)| {
       let _ = f.write_fmt(format_args!(
         "Group:{} has {} rows \n",
         group.id,
@@ -54,8 +54,9 @@ pub struct GroupContext<C> {
   /// The grouping field
   field: Arc<Field>,
 
-  /// Cache all the groups
-  groups_map: IndexMap<String, GroupData>,
+  /// 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<String, GroupData>,
 
   /// A reader that implement the [GroupSettingReader] trait
   ///
@@ -93,7 +94,7 @@ where
     Ok(Self {
       view_id,
       field,
-      groups_map: IndexMap::new(),
+      group_by_id: IndexMap::new(),
       reader,
       writer,
       setting,
@@ -105,26 +106,26 @@ where
   ///
   /// We take the `id` of the `field` as the no status group id
   pub(crate) fn get_no_status_group(&self) -> Option<&GroupData> {
-    self.groups_map.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.groups_map.get_mut(&self.field.id)
+    self.group_by_id.get_mut(&self.field.id)
   }
 
   pub(crate) fn groups(&self) -> Vec<&GroupData> {
-    self.groups_map.values().collect()
+    self.group_by_id.values().collect()
   }
 
   pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut GroupData> {
-    self.groups_map.get_mut(group_id)
+    self.group_by_id.get_mut(group_id)
   }
 
   // Returns the index and group specified by the group_id
   pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &GroupData)> {
     match (
-      self.groups_map.get_index_of(group_id),
-      self.groups_map.get(group_id),
+      self.group_by_id.get_index_of(group_id),
+      self.group_by_id.get(group_id),
     ) {
       (Some(index), Some(group)) => Some((index, group)),
       _ => None,
@@ -133,7 +134,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.groups_map.iter_mut().for_each(|(_, group)| {
+    self.group_by_id.iter_mut().for_each(|(_, group)| {
       if group.id != self.field.id {
         each(group);
       }
@@ -141,7 +142,7 @@ where
   }
 
   pub(crate) fn iter_mut_groups(&mut self, mut each: impl FnMut(&mut GroupData)) {
-    self.groups_map.iter_mut().for_each(|(_, group)| {
+    self.group_by_id.iter_mut().for_each(|(_, group)| {
       each(group);
     });
   }
@@ -153,7 +154,7 @@ where
       group.name.clone(),
       group.id.clone(),
     );
-    self.groups_map.insert(group.id.clone(), group_data);
+    self.group_by_id.insert(group.id.clone(), group_data);
     let (index, group_data) = self.get_group(&group.id).unwrap();
     let insert_group = InsertedGroupPB {
       group: GroupPB::from(group_data.clone()),
@@ -170,7 +171,7 @@ where
 
   #[tracing::instrument(level = "trace", skip(self))]
   pub(crate) fn delete_group(&mut self, deleted_group_id: &str) -> FlowyResult<()> {
-    self.groups_map.remove(deleted_group_id);
+    self.group_by_id.remove(deleted_group_id);
     self.mut_configuration(|configuration| {
       configuration
         .groups
@@ -181,11 +182,11 @@ where
   }
 
   pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> {
-    let from_index = self.groups_map.get_index_of(from_id);
-    let to_index = self.groups_map.get_index_of(to_id);
+    let from_index = self.group_by_id.get_index_of(from_id);
+    let to_index = self.group_by_id.get_index_of(to_id);
     match (from_index, to_index) {
       (Some(from_index), Some(to_index)) => {
-        self.groups_map.move_index(from_index, to_index);
+        self.group_by_id.move_index(from_index, to_index);
 
         self.mut_configuration(|configuration| {
           let from_index = configuration
@@ -205,7 +206,7 @@ where
             let group = configuration.groups.remove(*from);
             configuration.groups.insert(*to, group);
           }
-          tracing::debug!(
+          tracing::trace!(
             "Group order: {:?} ",
             configuration
               .groups
@@ -237,15 +238,15 @@ where
   /// [GroupConfigurationRevision] as old groups. The old groups and the new groups will be merged
   /// while keeping the order of the old groups.
   ///
-  #[tracing::instrument(level = "trace", skip(self, generated_group_context), err)]
+  #[tracing::instrument(level = "trace", skip_all, err)]
   pub(crate) fn init_groups(
     &mut self,
-    generated_group_context: GeneratedGroupContext,
-  ) -> FlowyResult<Option<GroupChangesetPB>> {
-    let GeneratedGroupContext {
+    generated_groups: GeneratedGroups,
+  ) -> FlowyResult<Option<GroupChangesPB>> {
+    let GeneratedGroups {
       no_status_group,
       group_configs,
-    } = generated_group_context;
+    } = generated_groups;
 
     let mut new_groups = vec![];
     let mut filter_content_map = HashMap::new();
@@ -310,18 +311,13 @@ where
     })?;
 
     // Update the memory cache of the groups
-    all_groups.into_iter().for_each(|group_rev| {
+    all_groups.into_iter().for_each(|group| {
       let filter_content = filter_content_map
-        .get(&group_rev.id)
+        .get(&group.id)
         .cloned()
         .unwrap_or_else(|| "".to_owned());
-      let group = GroupData::new(
-        group_rev.id,
-        self.field.id.clone(),
-        group_rev.name,
-        filter_content,
-      );
-      self.groups_map.insert(group.id.clone(), group);
+      let group = GroupData::new(group.id, self.field.id.clone(), group.name, filter_content);
+      self.group_by_id.insert(group.id.clone(), group);
     });
 
     let initial_groups = new_groups
@@ -338,13 +334,14 @@ where
       })
       .collect();
 
-    let changeset = GroupChangesetPB {
+    let changeset = GroupChangesPB {
       view_id: self.view_id.clone(),
       initial_groups,
       deleted_groups: deleted_group_ids,
       update_groups: vec![],
       inserted_groups: vec![],
     };
+
     tracing::trace!("Group changeset: {:?}", changeset);
     if changeset.is_empty() {
       Ok(None)
@@ -353,18 +350,15 @@ where
     }
   }
 
-  #[allow(dead_code)]
-  pub(crate) async fn hide_group(&mut self, group_id: &str) -> FlowyResult<()> {
-    self.mut_group_rev(group_id, |group_rev| {
-      group_rev.visible = false;
-    })?;
-    Ok(())
-  }
+  pub(crate) fn update_group(&mut self, group_changeset: GroupChangeset) -> FlowyResult<()> {
+    self.mut_group(&group_changeset.group_id, |group| {
+      if let Some(visible) = group_changeset.visible {
+        group.visible = visible;
+      }
 
-  #[allow(dead_code)]
-  pub(crate) async fn show_group(&mut self, group_id: &str) -> FlowyResult<()> {
-    self.mut_group_rev(group_id, |group_rev| {
-      group_rev.visible = true;
+      if let Some(name) = &group_changeset.name {
+        group.name = name.clone();
+      }
     })?;
     Ok(())
   }
@@ -398,11 +392,7 @@ where
     Ok(())
   }
 
-  fn mut_group_rev(
-    &mut self,
-    group_id: &str,
-    mut_groups_fn: impl Fn(&mut Group),
-  ) -> FlowyResult<()> {
+  fn mut_group(&mut self, group_id: &str, mut_groups_fn: impl Fn(&mut Group)) -> FlowyResult<()> {
     self.mut_configuration(|configuration| {
       match configuration
         .groups

+ 45 - 30
frontend/rust-lib/flowy-database2/src/services/group/controller.rs

@@ -9,14 +9,14 @@ use serde::Serialize;
 
 use flowy_error::FlowyResult;
 
-use crate::entities::{FieldType, GroupChangesetPB, GroupRowsNotificationPB, InsertedRowPB};
+use crate::entities::{FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB};
 use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser, DecodedCellData};
 use crate::services::group::action::{
-  DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions, GroupCustomize,
+  DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize,
 };
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::entities::GroupData;
-use crate::services::group::Group;
+use crate::services::group::{Group, GroupSettingChangeset};
 
 // use collab_database::views::Group;
 
@@ -28,24 +28,30 @@ use crate::services::group::Group;
 /// If the [FieldType] doesn't implement its group controller, then the [DefaultGroupController] will
 /// be used.
 ///
-pub trait GroupController: GroupControllerActions + Send + Sync {
+pub trait GroupController: GroupControllerOperation + Send + Sync {
+  /// Called when the type option of the [Field] was updated.
+  fn did_update_field_type_option(&mut self, field: &Arc<Field>);
+
+  /// Called before the row was created.
   fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str);
+
+  /// Called after the row was created.
   fn did_create_row(&mut self, row: &Row, group_id: &str);
 }
 
-/// The [GroupGenerator] trait is used to generate the groups for different [FieldType]
-pub trait GroupGenerator {
+/// The [GroupsBuilder] trait is used to generate the groups for different [FieldType]
+pub trait GroupsBuilder {
   type Context;
   type TypeOptionType;
 
-  fn generate_groups(
+  fn build(
     field: &Field,
-    group_ctx: &Self::Context,
+    context: &Self::Context,
     type_option: &Option<Self::TypeOptionType>,
-  ) -> GeneratedGroupContext;
+  ) -> GeneratedGroups;
 }
 
-pub struct GeneratedGroupContext {
+pub struct GeneratedGroups {
   pub no_status_group: Option<Group>,
   pub group_configs: Vec<GeneratedGroupConfig>,
 }
@@ -90,21 +96,21 @@ impl RowChangeset {
 
 /// C: represents the group configuration that impl [GroupConfigurationSerde]
 /// T: the type-option data deserializer that impl [TypeOptionDataDeserializer]
-/// G: the group generator, [GroupGenerator]
+/// G: the group generator, [GroupsBuilder]
 /// P: the parser that impl [CellProtobufBlobParser] for the CellBytes
-pub struct GenericGroupController<C, T, G, P> {
+pub struct BaseGroupController<C, T, G, P> {
   pub grouping_field_id: String,
   pub type_option: Option<T>,
-  pub group_ctx: GroupContext<C>,
+  pub context: GroupContext<C>,
   group_action_phantom: PhantomData<G>,
   cell_parser_phantom: PhantomData<P>,
 }
 
-impl<C, T, G, P> GenericGroupController<C, T, G, P>
+impl<C, T, G, P> BaseGroupController<C, T, G, P>
 where
   C: Serialize + DeserializeOwned,
   T: From<TypeOptionData>,
-  G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
+  G: GroupsBuilder<Context = GroupContext<C>, TypeOptionType = T>,
 {
   pub async fn new(
     grouping_field: &Arc<Field>,
@@ -112,13 +118,13 @@ where
   ) -> FlowyResult<Self> {
     let field_type = FieldType::from(grouping_field.field_type);
     let type_option = grouping_field.get_type_option::<T>(field_type);
-    let generated_group_context = G::generate_groups(grouping_field, &configuration, &type_option);
-    let _ = configuration.init_groups(generated_group_context)?;
+    let generated_groups = G::build(grouping_field, &configuration, &type_option);
+    let _ = configuration.init_groups(generated_groups)?;
 
     Ok(Self {
       grouping_field_id: grouping_field.id.clone(),
       type_option,
-      group_ctx: configuration,
+      context: configuration,
       group_action_phantom: PhantomData,
       cell_parser_phantom: PhantomData,
     })
@@ -131,7 +137,7 @@ where
     row: &Row,
     other_group_changesets: &[GroupRowsNotificationPB],
   ) -> Option<GroupRowsNotificationPB> {
-    let no_status_group = self.group_ctx.get_mut_no_status_group()?;
+    let no_status_group = self.context.get_mut_no_status_group()?;
 
     // [other_group_inserted_row] contains all the inserted rows except the default group.
     let other_group_inserted_row = other_group_changesets
@@ -196,12 +202,12 @@ where
   }
 }
 
-impl<C, T, G, P> GroupControllerActions for GenericGroupController<C, T, G, P>
+impl<C, T, G, P> GroupControllerOperation for BaseGroupController<C, T, G, P>
 where
   P: CellProtobufBlobParser,
   C: Serialize + DeserializeOwned,
   T: From<TypeOptionData>,
-  G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
+  G: GroupsBuilder<Context = GroupContext<C>, TypeOptionType = T>,
 
   Self: GroupCustomize<CellData = P::Object>,
 {
@@ -210,11 +216,11 @@ where
   }
 
   fn groups(&self) -> Vec<&GroupData> {
-    self.group_ctx.groups()
+    self.context.groups()
   }
 
   fn get_group(&self, group_id: &str) -> Option<(usize, GroupData)> {
-    let group = self.group_ctx.get_group(group_id)?;
+    let group = self.context.get_group(group_id)?;
     Some((group.0, group.1.clone()))
   }
 
@@ -230,7 +236,7 @@ where
         let mut grouped_rows: Vec<GroupedRow> = vec![];
         let cell_bytes = get_cell_protobuf(&cell, field, None);
         let cell_data = cell_bytes.parser::<P>()?;
-        for group in self.group_ctx.groups() {
+        for group in self.context.groups() {
           if self.can_group(&group.filter_content, &cell_data) {
             grouped_rows.push(GroupedRow {
               row: (*row).clone(),
@@ -241,25 +247,25 @@ where
 
         if !grouped_rows.is_empty() {
           for group_row in grouped_rows {
-            if let Some(group) = self.group_ctx.get_mut_group(&group_row.group_id) {
+            if let Some(group) = self.context.get_mut_group(&group_row.group_id) {
               group.add_row(group_row.row);
             }
           }
           continue;
         }
       }
-      match self.group_ctx.get_mut_no_status_group() {
+      match self.context.get_mut_no_status_group() {
         None => {},
         Some(no_status_group) => no_status_group.add_row((*row).clone()),
       }
     }
 
-    tracing::Span::current().record("group_result", format!("{},", self.group_ctx,).as_str());
+    tracing::Span::current().record("group_result", format!("{},", self.context,).as_str());
     Ok(())
   }
 
   fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
-    self.group_ctx.move_group(from_group_id, to_group_id)
+    self.context.move_group(from_group_id, to_group_id)
   }
 
   fn did_update_group_row(
@@ -320,7 +326,7 @@ where
       }
     }
 
-    match self.group_ctx.get_no_status_group() {
+    match self.context.get_no_status_group() {
       None => {
         tracing::error!("Unexpected None value. It should have the no status group");
       },
@@ -359,9 +365,18 @@ where
     Ok(result)
   }
 
-  fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesetPB>> {
+  fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesPB>> {
     Ok(None)
   }
+
+  fn apply_group_setting_changeset(&mut self, changeset: GroupSettingChangeset) -> FlowyResult<()> {
+    for group_changeset in changeset.update_groups {
+      if let Err(e) = self.context.update_group(group_changeset) {
+        tracing::error!("Failed to update group: {:?}", e);
+      }
+    }
+    Ok(())
+  }
 }
 
 struct GroupedRow {

+ 18 - 13
frontend/rust-lib/flowy-database2/src/services/group/controller_impls/checkbox_controller.rs

@@ -1,6 +1,7 @@
 use collab_database::fields::Field;
 use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
 use serde::{Deserialize, Serialize};
+use std::sync::Arc;
 
 use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowPB};
 use crate::services::cell::insert_checkbox_cell;
@@ -10,16 +11,16 @@ use crate::services::field::{
 use crate::services::group::action::GroupCustomize;
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::controller::{
-  GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
+  BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
 };
-use crate::services::group::{move_group_row, GeneratedGroupConfig, GeneratedGroupContext, Group};
+use crate::services::group::{move_group_row, GeneratedGroupConfig, GeneratedGroups, Group};
 
 #[derive(Default, Serialize, Deserialize)]
 pub struct CheckboxGroupConfiguration {
   pub hide_empty: bool,
 }
 
-pub type CheckboxGroupController = GenericGroupController<
+pub type CheckboxGroupController = BaseGroupController<
   CheckboxGroupConfiguration,
   CheckboxTypeOption,
   CheckboxGroupGenerator,
@@ -52,7 +53,7 @@ impl GroupCustomize for CheckboxGroupController {
     cell_data: &Self::CellData,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
       let is_not_contained = !group.contains_row(&row.id);
       if group.id == CHECK {
@@ -96,7 +97,7 @@ impl GroupCustomize for CheckboxGroupController {
 
   fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
       if group.contains_row(&row.id) {
         changeset.deleted_rows.push(row.id.clone().into_inner());
@@ -116,7 +117,7 @@ impl GroupCustomize for CheckboxGroupController {
     mut context: MoveGroupRowContext,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut group_changeset = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       if let Some(changeset) = move_group_row(group, &mut context) {
         group_changeset.push(changeset);
       }
@@ -126,8 +127,12 @@ impl GroupCustomize for CheckboxGroupController {
 }
 
 impl GroupController for CheckboxGroupController {
+  fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {
+    // Do nothing
+  }
+
   fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
-    match self.group_ctx.get_group(group_id) {
+    match self.context.get_group(group_id) {
       None => tracing::warn!("Can not find the group: {}", group_id),
       Some((_, group)) => {
         let is_check = group.id == CHECK;
@@ -138,22 +143,22 @@ impl GroupController for CheckboxGroupController {
   }
 
   fn did_create_row(&mut self, row: &Row, group_id: &str) {
-    if let Some(group) = self.group_ctx.get_mut_group(group_id) {
+    if let Some(group) = self.context.get_mut_group(group_id) {
       group.add_row(row.clone())
     }
   }
 }
 
 pub struct CheckboxGroupGenerator();
-impl GroupGenerator for CheckboxGroupGenerator {
+impl GroupsBuilder for CheckboxGroupGenerator {
   type Context = CheckboxGroupContext;
   type TypeOptionType = CheckboxTypeOption;
 
-  fn generate_groups(
+  fn build(
     _field: &Field,
-    _group_ctx: &Self::Context,
+    _context: &Self::Context,
     _type_option: &Option<Self::TypeOptionType>,
-  ) -> GeneratedGroupContext {
+  ) -> GeneratedGroups {
     let check_group = GeneratedGroupConfig {
       group: Group::new(CHECK.to_string(), "".to_string()),
       filter_content: CHECK.to_string(),
@@ -164,7 +169,7 @@ impl GroupGenerator for CheckboxGroupGenerator {
       filter_content: UNCHECK.to_string(),
     };
 
-    GeneratedGroupContext {
+    GeneratedGroups {
       no_status_group: None,
       group_configs: vec![check_group, uncheck_group],
     }

+ 18 - 5
frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs

@@ -5,11 +5,13 @@ use collab_database::rows::{Cells, Row};
 
 use flowy_error::FlowyResult;
 
-use crate::entities::GroupChangesetPB;
+use crate::entities::GroupChangesPB;
 use crate::services::group::action::{
-  DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions,
+  DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation,
+};
+use crate::services::group::{
+  GroupController, GroupData, GroupSettingChangeset, MoveGroupRowContext,
 };
-use crate::services::group::{GroupController, 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
@@ -37,7 +39,7 @@ impl DefaultGroupController {
   }
 }
 
-impl GroupControllerActions for DefaultGroupController {
+impl GroupControllerOperation for DefaultGroupController {
   fn field_id(&self) -> &str {
     &self.field_id
   }
@@ -95,12 +97,23 @@ impl GroupControllerActions for DefaultGroupController {
     })
   }
 
-  fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesetPB>> {
+  fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesPB>> {
     Ok(None)
   }
+
+  fn apply_group_setting_changeset(
+    &mut self,
+    _changeset: GroupSettingChangeset,
+  ) -> FlowyResult<()> {
+    Ok(())
+  }
 }
 
 impl GroupController for DefaultGroupController {
+  fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {
+    // Do nothing
+  }
+
   fn will_create_row(&mut self, _cells: &mut Cells, _field: &Field, _group_id: &str) {}
 
   fn did_create_row(&mut self, _row: &Row, _group_id: &str) {}

+ 17 - 14
frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs

@@ -3,14 +3,15 @@ use crate::services::cell::insert_select_option_cell;
 use crate::services::field::{MultiSelectTypeOption, SelectOptionCellDataParser};
 use crate::services::group::action::GroupCustomize;
 use crate::services::group::controller::{
-  GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
+  BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
 };
 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, GeneratedGroupContext, GroupContext,
+  move_group_row, remove_select_option_row, GeneratedGroups, GroupContext,
 };
 use collab_database::fields::Field;
 use collab_database::rows::{Cells, Row};
+use std::sync::Arc;
 
 use serde::{Deserialize, Serialize};
 
@@ -21,7 +22,7 @@ pub struct MultiSelectGroupConfiguration {
 
 pub type MultiSelectOptionGroupContext = GroupContext<MultiSelectGroupConfiguration>;
 // MultiSelect
-pub type MultiSelectGroupController = GenericGroupController<
+pub type MultiSelectGroupController = BaseGroupController<
   MultiSelectGroupConfiguration,
   MultiSelectTypeOption,
   MultiSelectGroupGenerator,
@@ -44,7 +45,7 @@ impl GroupCustomize for MultiSelectGroupController {
     cell_data: &Self::CellData,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row) {
         changesets.push(changeset);
       }
@@ -54,7 +55,7 @@ impl GroupCustomize for MultiSelectGroupController {
 
   fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
         changesets.push(changeset);
       }
@@ -68,7 +69,7 @@ impl GroupCustomize for MultiSelectGroupController {
     mut context: MoveGroupRowContext,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut group_changeset = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       if let Some(changeset) = move_group_row(group, &mut context) {
         group_changeset.push(changeset);
       }
@@ -78,8 +79,10 @@ impl GroupCustomize for MultiSelectGroupController {
 }
 
 impl GroupController for MultiSelectGroupController {
+  fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
+
   fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
-    match self.group_ctx.get_group(group_id) {
+    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);
@@ -89,28 +92,28 @@ impl GroupController for MultiSelectGroupController {
   }
 
   fn did_create_row(&mut self, row: &Row, group_id: &str) {
-    if let Some(group) = self.group_ctx.get_mut_group(group_id) {
+    if let Some(group) = self.context.get_mut_group(group_id) {
       group.add_row(row.clone())
     }
   }
 }
 
-pub struct MultiSelectGroupGenerator();
-impl GroupGenerator for MultiSelectGroupGenerator {
+pub struct MultiSelectGroupGenerator;
+impl GroupsBuilder for MultiSelectGroupGenerator {
   type Context = MultiSelectOptionGroupContext;
   type TypeOptionType = MultiSelectTypeOption;
 
-  fn generate_groups(
+  fn build(
     field: &Field,
-    _group_ctx: &Self::Context,
+    _context: &Self::Context,
     type_option: &Option<Self::TypeOptionType>,
-  ) -> GeneratedGroupContext {
+  ) -> GeneratedGroups {
     let group_configs = match type_option {
       None => vec![],
       Some(type_option) => generate_select_option_groups(&field.id, &type_option.options),
     };
 
-    GeneratedGroupContext {
+    GeneratedGroups {
       no_status_group: Some(make_no_status_group(field)),
       group_configs,
     }

+ 16 - 13
frontend/rust-lib/flowy-database2/src/services/group/controller_impls/select_option_controller/single_select_controller.rs

@@ -4,13 +4,14 @@ use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption}
 use crate::services::group::action::GroupCustomize;
 use collab_database::fields::Field;
 use collab_database::rows::{Cells, Row};
+use std::sync::Arc;
 
 use crate::services::group::controller::{
-  GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
+  BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
 };
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::GroupData;
-use crate::services::group::{make_no_status_group, GeneratedGroupContext, GroupContext};
+use crate::services::group::{make_no_status_group, GeneratedGroups, GroupContext};
 
 use serde::{Deserialize, Serialize};
 
@@ -22,7 +23,7 @@ pub struct SingleSelectGroupConfiguration {
 pub type SingleSelectOptionGroupContext = GroupContext<SingleSelectGroupConfiguration>;
 
 // SingleSelect
-pub type SingleSelectGroupController = GenericGroupController<
+pub type SingleSelectGroupController = BaseGroupController<
   SingleSelectGroupConfiguration,
   SingleSelectTypeOption,
   SingleSelectGroupGenerator,
@@ -44,7 +45,7 @@ impl GroupCustomize for SingleSelectGroupController {
     cell_data: &Self::CellData,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row) {
         changesets.push(changeset);
       }
@@ -54,7 +55,7 @@ impl GroupCustomize for SingleSelectGroupController {
 
   fn delete_row(&mut self, row: &Row, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       if let Some(changeset) = remove_select_option_row(group, cell_data, row) {
         changesets.push(changeset);
       }
@@ -68,7 +69,7 @@ impl GroupCustomize for SingleSelectGroupController {
     mut context: MoveGroupRowContext,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut group_changeset = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       if let Some(changeset) = move_group_row(group, &mut context) {
         group_changeset.push(changeset);
       }
@@ -78,8 +79,10 @@ impl GroupCustomize for SingleSelectGroupController {
 }
 
 impl GroupController for SingleSelectGroupController {
+  fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
+
   fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
-    let group: Option<&mut GroupData> = self.group_ctx.get_mut_group(group_id);
+    let group: Option<&mut GroupData> = self.context.get_mut_group(group_id);
     match group {
       None => {},
       Some(group) => {
@@ -89,27 +92,27 @@ impl GroupController for SingleSelectGroupController {
     }
   }
   fn did_create_row(&mut self, row: &Row, group_id: &str) {
-    if let Some(group) = self.group_ctx.get_mut_group(group_id) {
+    if let Some(group) = self.context.get_mut_group(group_id) {
       group.add_row(row.clone())
     }
   }
 }
 
 pub struct SingleSelectGroupGenerator();
-impl GroupGenerator for SingleSelectGroupGenerator {
+impl GroupsBuilder for SingleSelectGroupGenerator {
   type Context = SingleSelectOptionGroupContext;
   type TypeOptionType = SingleSelectTypeOption;
-  fn generate_groups(
+  fn build(
     field: &Field,
-    _group_ctx: &Self::Context,
+    _context: &Self::Context,
     type_option: &Option<Self::TypeOptionType>,
-  ) -> GeneratedGroupContext {
+  ) -> GeneratedGroups {
     let group_configs = match type_option {
       None => vec![],
       Some(type_option) => generate_select_option_groups(&field.id, &type_option.options),
     };
 
-    GeneratedGroupContext {
+    GeneratedGroups {
       no_status_group: Some(make_no_status_group(field)),
       group_configs,
     }

+ 24 - 25
frontend/rust-lib/flowy-database2/src/services/group/controller_impls/url_controller.rs

@@ -1,6 +1,7 @@
 use collab_database::fields::Field;
 use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
 use serde::{Deserialize, Serialize};
+use std::sync::Arc;
 
 use flowy_error::FlowyResult;
 
@@ -12,10 +13,10 @@ use crate::services::field::{URLCellData, URLCellDataParser, URLTypeOption};
 use crate::services::group::action::GroupCustomize;
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::controller::{
-  GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
+  BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
 };
 use crate::services::group::{
-  make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroupContext, Group,
+  make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroups, Group,
 };
 
 #[derive(Default, Serialize, Deserialize)]
@@ -23,12 +24,8 @@ pub struct URLGroupConfiguration {
   pub hide_empty: bool,
 }
 
-pub type URLGroupController = GenericGroupController<
-  URLGroupConfiguration,
-  URLTypeOption,
-  URLGroupGenerator,
-  URLCellDataParser,
->;
+pub type URLGroupController =
+  BaseGroupController<URLGroupConfiguration, URLTypeOption, URLGroupGenerator, URLCellDataParser>;
 
 pub type URLGroupContext = GroupContext<URLGroupConfiguration>;
 
@@ -55,17 +52,17 @@ impl GroupCustomize for URLGroupController {
   ) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
     // Just return if the group with this url already exists
     let mut inserted_group = None;
-    if self.group_ctx.get_group(&_cell_data.url).is_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 mut new_group = self.group_ctx.add_new_group(group)?;
+      let mut new_group = self.context.add_new_group(group)?;
       new_group.group.rows.push(RowPB::from(row));
       inserted_group = Some(new_group);
     }
 
     // Delete the old url group if there are no rows in that group
     let deleted_group = match _old_cell_data
-      .and_then(|old_cell_data| self.group_ctx.get_group(&old_cell_data.content))
+      .and_then(|old_cell_data| self.context.get_group(&old_cell_data.content))
     {
       None => None,
       Some((_, group)) => {
@@ -80,7 +77,7 @@ impl GroupCustomize for URLGroupController {
     let deleted_group = match deleted_group {
       None => None,
       Some(group) => {
-        self.group_ctx.delete_group(&group.id)?;
+        self.context.delete_group(&group.id)?;
         Some(GroupPB::from(group.clone()))
       },
     };
@@ -94,7 +91,7 @@ impl GroupCustomize for URLGroupController {
     cell_data: &Self::CellData,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_status_groups(|group| {
+    self.context.iter_mut_status_groups(|group| {
       let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
       if group.id == cell_data.content {
         if !group.contains_row(&row.id) {
@@ -117,7 +114,7 @@ impl GroupCustomize for URLGroupController {
 
   fn delete_row(&mut self, row: &Row, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
     let mut changesets = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
       if group.contains_row(&row.id) {
         group.remove_row(&row.id);
@@ -137,7 +134,7 @@ impl GroupCustomize for URLGroupController {
     mut context: MoveGroupRowContext,
   ) -> Vec<GroupRowsNotificationPB> {
     let mut group_changeset = vec![];
-    self.group_ctx.iter_mut_groups(|group| {
+    self.context.iter_mut_groups(|group| {
       if let Some(changeset) = move_group_row(group, &mut context) {
         group_changeset.push(changeset);
       }
@@ -151,14 +148,14 @@ impl GroupCustomize for URLGroupController {
     _cell_data: &Self::CellData,
   ) -> Option<GroupPB> {
     let mut deleted_group = None;
-    if let Some((_, group)) = self.group_ctx.get_group(&_cell_data.content) {
+    if let Some((_, group)) = self.context.get_group(&_cell_data.content) {
       if group.rows.len() == 1 {
         deleted_group = Some(GroupPB::from(group.clone()));
       }
     }
     if deleted_group.is_some() {
       let _ = self
-        .group_ctx
+        .context
         .delete_group(&deleted_group.as_ref().unwrap().group_id);
     }
     deleted_group
@@ -166,8 +163,10 @@ impl GroupCustomize for URLGroupController {
 }
 
 impl GroupController for URLGroupController {
+  fn did_update_field_type_option(&mut self, _field: &Arc<Field>) {}
+
   fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str) {
-    match self.group_ctx.get_group(group_id) {
+    match self.context.get_group(group_id) {
       None => tracing::warn!("Can not find the group: {}", group_id),
       Some((_, group)) => {
         let cell = insert_url_cell(group.id.clone(), field);
@@ -177,24 +176,24 @@ impl GroupController for URLGroupController {
   }
 
   fn did_create_row(&mut self, row: &Row, group_id: &str) {
-    if let Some(group) = self.group_ctx.get_mut_group(group_id) {
+    if let Some(group) = self.context.get_mut_group(group_id) {
       group.add_row(row.clone())
     }
   }
 }
 
 pub struct URLGroupGenerator();
-impl GroupGenerator for URLGroupGenerator {
+impl GroupsBuilder for URLGroupGenerator {
   type Context = URLGroupContext;
   type TypeOptionType = URLTypeOption;
 
-  fn generate_groups(
+  fn build(
     field: &Field,
-    group_ctx: &Self::Context,
+    context: &Self::Context,
     _type_option: &Option<Self::TypeOptionType>,
-  ) -> GeneratedGroupContext {
+  ) -> GeneratedGroups {
     // Read all the cells for the grouping field
-    let cells = futures::executor::block_on(group_ctx.get_all_cells());
+    let cells = futures::executor::block_on(context.get_all_cells());
 
     // Generate the groups
     let group_configs = cells
@@ -208,7 +207,7 @@ impl GroupGenerator for URLGroupGenerator {
       .collect();
 
     let no_status_group = Some(make_no_status_group(field));
-    GeneratedGroupContext {
+    GeneratedGroups {
       no_status_group,
       group_configs,
     }

+ 12 - 2
frontend/rust-lib/flowy-database2/src/services/group/entities.rs

@@ -14,6 +14,16 @@ pub struct GroupSetting {
   pub content: String,
 }
 
+pub struct GroupSettingChangeset {
+  pub update_groups: Vec<GroupChangeset>,
+}
+
+pub struct GroupChangeset {
+  pub group_id: String,
+  pub name: Option<String>,
+  pub visible: Option<bool>,
+}
+
 impl GroupSetting {
   pub fn new(field_id: String, field_type: i64, content: String) -> Self {
     Self {
@@ -75,7 +85,7 @@ impl From<GroupSetting> for GroupSettingMap {
 pub struct Group {
   pub id: String,
   pub name: String,
-  #[serde(default = "GROUP_REV_VISIBILITY")]
+  #[serde(default = "GROUP_VISIBILITY")]
   pub visible: bool,
 }
 
@@ -104,7 +114,7 @@ impl From<Group> for GroupMap {
   }
 }
 
-const GROUP_REV_VISIBILITY: fn() -> bool = || true;
+const GROUP_VISIBILITY: fn() -> bool = || true;
 
 impl Group {
   pub fn new(id: String, name: String) -> Self {

+ 1 - 1
frontend/rust-lib/flowy-database2/src/services/group/group_util.rs

@@ -42,7 +42,7 @@ where
   W: GroupSettingWriter,
 {
   let grouping_field_type = FieldType::from(grouping_field.field_type);
-  tracing::Span::current().record("grouping_field_type", &grouping_field_type.default_name());
+  tracing::Span::current().record("grouping_field", &grouping_field_type.default_name());
 
   let mut group_controller: Box<dyn GroupController>;
   let configuration_reader = Arc::new(setting_reader);

+ 4 - 2
frontend/rust-lib/flowy-database2/src/services/sort/controller.rs

@@ -82,7 +82,9 @@ impl SortController {
 
   pub async fn did_receive_row_changed(&self, row_id: RowId) {
     let task_type = SortEvent::RowDidChanged(row_id);
-    self.gen_task(task_type, QualityOfService::Background).await;
+    if !self.sorts.is_empty() {
+      self.gen_task(task_type, QualityOfService::Background).await;
+    }
   }
 
   // #[tracing::instrument(name = "process_sort_task", level = "trace", skip_all, err)]
@@ -169,7 +171,7 @@ impl SortController {
       .await;
   }
 
-  pub async fn did_update_view_field_type_option(&self, _field_rev: &Field) {
+  pub async fn did_update_field_type_option(&self, _field: &Field) {
     //
   }
 

+ 12 - 12
frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs

@@ -10,7 +10,7 @@ use collab_database::rows::{Row, RowId};
 use futures::TryFutureExt;
 use tokio::sync::broadcast::Receiver;
 
-use flowy_database2::entities::{AlterFilterParams, AlterFilterPayloadPB, CheckboxFilterConditionPB, CheckboxFilterPB, ChecklistFilterConditionPB, ChecklistFilterPB, DatabaseViewSettingPB, DateFilterConditionPB, DateFilterPB, DeleteFilterParams, FieldType, FilterPB, NumberFilterConditionPB, NumberFilterPB, SelectOptionConditionPB, SelectOptionFilterPB, TextFilterConditionPB, TextFilterPB};
+use flowy_database2::entities::{UpdateFilterParams, UpdateFilterPayloadPB, CheckboxFilterConditionPB, CheckboxFilterPB, ChecklistFilterConditionPB, ChecklistFilterPB, DatabaseViewSettingPB, DateFilterConditionPB, DateFilterPB, DeleteFilterParams, FieldType, FilterPB, NumberFilterConditionPB, NumberFilterPB, SelectOptionConditionPB, SelectOptionFilterPB, TextFilterConditionPB, TextFilterPB};
 use flowy_database2::services::database_view::DatabaseViewChanged;
 use flowy_database2::services::filter::FilterType;
 
@@ -33,7 +33,7 @@ pub enum FilterScript {
         changed: Option<FilterRowChanged>,
     },
     InsertFilter {
-        payload: AlterFilterPayloadPB,
+        payload: UpdateFilterPayloadPB,
     },
     CreateTextFilter {
         condition: TextFilterConditionPB,
@@ -151,7 +151,7 @@ impl DatabaseFilterTest {
                     content
                 };
                 let payload =
-                    AlterFilterPayloadPB::new(
+                    UpdateFilterPayloadPB::new(
                         & self.view_id(),
                         &field, text_filter);
                 self.insert_filter(payload).await;
@@ -159,7 +159,7 @@ impl DatabaseFilterTest {
             FilterScript::UpdateTextFilter { filter, condition, content, changed} => {
                 self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
                 self.assert_future_changed(changed).await;
-                let params = AlterFilterParams {
+                let params = UpdateFilterParams {
                     view_id: self.view_id(),
                     field_id: filter.field_id,
                     filter_id: Some(filter.id),
@@ -178,7 +178,7 @@ impl DatabaseFilterTest {
                     content
                 };
                 let payload =
-                    AlterFilterPayloadPB::new(
+                    UpdateFilterPayloadPB::new(
                         &self.view_id(),
                         &field, number_filter);
                 self.insert_filter(payload).await;
@@ -191,7 +191,7 @@ impl DatabaseFilterTest {
                     condition
                 };
                 let payload =
-                    AlterFilterPayloadPB::new(& self.view_id(), &field, checkbox_filter);
+                    UpdateFilterPayloadPB::new(& self.view_id(), &field, checkbox_filter);
                 self.insert_filter(payload).await;
             }
             FilterScript::CreateDateFilter { condition, start, end, timestamp, changed} => {
@@ -206,7 +206,7 @@ impl DatabaseFilterTest {
                 };
 
                 let payload =
-                    AlterFilterPayloadPB::new(&self.view_id(), &field, date_filter);
+                    UpdateFilterPayloadPB::new(&self.view_id(), &field, date_filter);
                 self.insert_filter(payload).await;
             }
             FilterScript::CreateMultiSelectFilter { condition, option_ids} => {
@@ -214,7 +214,7 @@ impl DatabaseFilterTest {
                 let field = self.get_first_field(FieldType::MultiSelect);
                 let filter = SelectOptionFilterPB { condition, option_ids };
                 let payload =
-                    AlterFilterPayloadPB::new(&self.view_id(), &field, filter);
+                    UpdateFilterPayloadPB::new(&self.view_id(), &field, filter);
                 self.insert_filter(payload).await;
             }
             FilterScript::CreateSingleSelectFilter { condition, option_ids, changed} => {
@@ -223,7 +223,7 @@ impl DatabaseFilterTest {
                 let field = self.get_first_field(FieldType::SingleSelect);
                 let filter = SelectOptionFilterPB { condition, option_ids };
                 let payload =
-                    AlterFilterPayloadPB::new(& self.view_id(), &field, filter);
+                    UpdateFilterPayloadPB::new(& self.view_id(), &field, filter);
                 self.insert_filter(payload).await;
             }
             FilterScript::CreateChecklistFilter { condition,changed} => {
@@ -233,7 +233,7 @@ impl DatabaseFilterTest {
                 // let type_option = self.get_checklist_type_option(&field_rev.id);
                 let filter = ChecklistFilterPB { condition };
                 let payload =
-                    AlterFilterPayloadPB::new(& self.view_id(), &field, filter);
+                    UpdateFilterPayloadPB::new(& self.view_id(), &field, filter);
                 self.insert_filter(payload).await;
             }
             FilterScript::AssertFilterCount { count } => {
@@ -289,8 +289,8 @@ impl DatabaseFilterTest {
 
     }
 
-    async fn insert_filter(&self, payload: AlterFilterPayloadPB) {
-        let params: AlterFilterParams = payload.try_into().unwrap();
+    async fn insert_filter(&self, payload: UpdateFilterPayloadPB) {
+        let params: UpdateFilterParams = payload.try_into().unwrap();
         let _ = self.editor.create_or_update_filter(params).await.unwrap();
     }
 

+ 2 - 2
frontend/rust-lib/flowy-database2/tests/database/filter_test/text_filter_test.rs

@@ -1,7 +1,7 @@
 use crate::database::filter_test::script::FilterScript::*;
 use crate::database::filter_test::script::*;
 use flowy_database2::entities::{
-  AlterFilterPayloadPB, FieldType, TextFilterConditionPB, TextFilterPB,
+  FieldType, TextFilterConditionPB, TextFilterPB, UpdateFilterPayloadPB,
 };
 use flowy_database2::services::filter::FilterType;
 
@@ -195,7 +195,7 @@ async fn grid_filter_delete_test() {
     condition: TextFilterConditionPB::TextIsEmpty,
     content: "".to_string(),
   };
-  let payload = AlterFilterPayloadPB::new(&test.view_id(), &field, text_filter);
+  let payload = UpdateFilterPayloadPB::new(&test.view_id(), &field, text_filter);
   let scripts = vec![
     InsertFilter { payload },
     AssertFilterCount { count: 1 },

+ 2 - 2
frontend/rust-lib/flowy-database2/tests/database/sort_test/script.rs

@@ -8,7 +8,7 @@ use collab_database::rows::RowId;
 use futures::stream::StreamExt;
 use tokio::sync::broadcast::Receiver;
 
-use flowy_database2::entities::{AlterSortParams, DeleteSortParams, FieldType};
+use flowy_database2::entities::{DeleteSortParams, FieldType, UpdateSortParams};
 use flowy_database2::services::cell::stringify_cell_data;
 use flowy_database2::services::database_view::DatabaseViewChanged;
 use flowy_database2::services::sort::{Sort, SortCondition, SortType};
@@ -72,7 +72,7 @@ impl DatabaseSortTest {
             .await
             .unwrap(),
         );
-        let params = AlterSortParams {
+        let params = UpdateSortParams {
           view_id: self.view_id.clone(),
           field_id: field.id.clone(),
           sort_id: None,

+ 34 - 20
frontend/rust-lib/lib-log/src/layer.rs

@@ -13,7 +13,7 @@ const MESSAGE: &str = "msg";
 const LOG_MODULE_PATH: &str = "log.module_path";
 const LOG_TARGET_PATH: &str = "log.target";
 
-const FLOWY_RESERVED_FIELDS: [&str; 3] = [LEVEL, TIME, MESSAGE];
+const RESERVED_FIELDS: [&str; 3] = [LEVEL, TIME, MESSAGE];
 const IGNORE_FIELDS: [&str; 2] = [LOG_MODULE_PATH, LOG_TARGET_PATH];
 
 pub struct FlowyFormattingLayer<W: MakeWriter + 'static> {
@@ -29,7 +29,7 @@ impl<W: MakeWriter + 'static> FlowyFormattingLayer<W> {
     }
   }
 
-  fn serialize_flowy_folder_fields(
+  fn serialize_fields(
     &self,
     map_serializer: &mut impl SerializeMap<Error = serde_json::Error>,
     message: &str,
@@ -45,12 +45,13 @@ impl<W: MakeWriter + 'static> FlowyFormattingLayer<W> {
     &self,
     span: &SpanRef<S>,
     ty: Type,
+    ctx: &Context<'_, S>,
   ) -> Result<Vec<u8>, std::io::Error> {
     let mut buffer = Vec::new();
     let mut serializer = serde_json::Serializer::new(&mut buffer);
     let mut map_serializer = serializer.serialize_map(None)?;
-    let message = format_span_context(span, ty);
-    self.serialize_flowy_folder_fields(&mut map_serializer, &message, span.metadata().level())?;
+    let message = format_span_context(span, ty, ctx);
+    self.serialize_fields(&mut map_serializer, &message, span.metadata().level())?;
     if self.with_target {
       map_serializer.serialize_entry("target", &span.metadata().target())?;
     }
@@ -61,7 +62,7 @@ impl<W: MakeWriter + 'static> FlowyFormattingLayer<W> {
     let extensions = span.extensions();
     if let Some(visitor) = extensions.get::<JsonStorage>() {
       for (key, value) in visitor.values() {
-        if !FLOWY_RESERVED_FIELDS.contains(key) && !IGNORE_FIELDS.contains(key) {
+        if !RESERVED_FIELDS.contains(key) && !IGNORE_FIELDS.contains(key) {
           map_serializer.serialize_entry(key, value)?;
         } else {
           tracing::debug!(
@@ -93,9 +94,9 @@ pub enum Type {
 impl fmt::Display for Type {
   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     let repr = match self {
-      Type::EnterSpan => "START",
-      Type::ExitSpan => "END",
-      Type::Event => "EVENT",
+      Type::EnterSpan => "Start",
+      Type::ExitSpan => "End",
+      Type::Event => "Event",
     };
     write!(f, "{}", repr)
   }
@@ -104,14 +105,27 @@ impl fmt::Display for Type {
 fn format_span_context<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>>(
   span: &SpanRef<S>,
   ty: Type,
+  context: &Context<'_, S>,
 ) -> String {
-  format!("[⛳ {} - {}]", span.metadata().name().to_uppercase(), ty)
+  match context.lookup_current() {
+    None => {
+      if matches!(ty, Type::EnterSpan) {
+        format!("[🟢 {} - {}]", span.metadata().name().to_uppercase(), ty)
+      } else {
+        format!("[🔵 {} - {}]", span.metadata().name().to_uppercase(), ty)
+      }
+    },
+    Some(_) => {
+      format!("[{} - {}]", span.metadata().name().to_uppercase(), ty)
+    },
+  }
 }
 
 fn format_event_message<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>>(
   current_span: &Option<SpanRef<S>>,
   event: &Event,
   event_visitor: &JsonStorage<'_>,
+  context: &Context<'_, S>,
 ) -> String {
   // Extract the "message" field, if provided. Fallback to the target, if missing.
   let mut message = event_visitor
@@ -127,7 +141,11 @@ fn format_event_message<S: Subscriber + for<'a> tracing_subscriber::registry::Lo
   // If the event is in the context of a span, prepend the span name to the
   // message.
   if let Some(span) = &current_span {
-    message = format!("{} {}", format_span_context(span, Type::Event), message);
+    message = format!(
+      "{} {}",
+      format_span_context(span, Type::Event, context),
+      message
+    );
   }
 
   message
@@ -154,12 +172,8 @@ where
       let mut serializer = serde_json::Serializer::new(&mut buffer);
       let mut map_serializer = serializer.serialize_map(None)?;
 
-      let message = format_event_message(&current_span, event, &event_visitor);
-      self.serialize_flowy_folder_fields(
-        &mut map_serializer,
-        &message,
-        event.metadata().level(),
-      )?;
+      let message = format_event_message(&current_span, event, &event_visitor, &ctx);
+      self.serialize_fields(&mut map_serializer, &message, event.metadata().level())?;
       // Additional metadata useful for debugging
       // They should be nested under `src` (see https://github.com/trentm/node-bunyan#src )
       // but `tracing` does not support nested values yet
@@ -174,7 +188,7 @@ where
       // Add all the other fields associated with the event, expect the message we
       // already used.
       for (key, value) in event_visitor.values().iter().filter(|(&key, _)| {
-        key != "message" && !FLOWY_RESERVED_FIELDS.contains(&key) && !IGNORE_FIELDS.contains(&key)
+        key != "message" && !RESERVED_FIELDS.contains(&key) && !IGNORE_FIELDS.contains(&key)
       }) {
         map_serializer.serialize_entry(key, value)?;
       }
@@ -184,7 +198,7 @@ where
         let extensions = span.extensions();
         if let Some(visitor) = extensions.get::<JsonStorage>() {
           for (key, value) in visitor.values() {
-            if !FLOWY_RESERVED_FIELDS.contains(key) && !IGNORE_FIELDS.contains(key) {
+            if !RESERVED_FIELDS.contains(key) && !IGNORE_FIELDS.contains(key) {
               map_serializer.serialize_entry(key, value)?;
             } else {
               tracing::debug!(
@@ -207,14 +221,14 @@ where
 
   fn new_span(&self, _attrs: &Attributes, id: &Id, ctx: Context<'_, S>) {
     let span = ctx.span(id).expect("Span not found, this is a bug");
-    if let Ok(serialized) = self.serialize_span(&span, Type::EnterSpan) {
+    if let Ok(serialized) = self.serialize_span(&span, Type::EnterSpan, &ctx) {
       let _ = self.emit(serialized);
     }
   }
 
   fn on_close(&self, id: Id, ctx: Context<'_, S>) {
     let span = ctx.span(&id).expect("Span not found, this is a bug");
-    if let Ok(serialized) = self.serialize_span(&span, Type::ExitSpan) {
+    if let Ok(serialized) = self.serialize_span(&span, Type::ExitSpan, &ctx) {
       let _ = self.emit(serialized);
     }
   }

+ 14 - 24
frontend/rust-lib/lib-log/src/lib.rs

@@ -42,30 +42,20 @@ impl Builder {
 
     let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
     let subscriber = tracing_subscriber::fmt()
-            // .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
-            .with_ansi(false)
-            .with_target(false)
-            .with_max_level(tracing::Level::TRACE)
-            .with_writer(std::io::stderr)
-            .with_thread_ids(true)
-            // .with_writer(non_blocking)
-            .json()
-            .with_current_span(true)
-            .with_span_list(true)
-            .compact()
-            .finish()
-            .with(env_filter)
-            .with(JsonStorageLayer)
-            .with(FlowyFormattingLayer::new(std::io::stdout))
-            .with(FlowyFormattingLayer::new(non_blocking));
-
-    // if cfg!(feature = "use_bunyan") {
-    //     let formatting_layer = BunyanFormattingLayer::new(self.name.clone(),
-    // std::io::stdout);     let _ =
-    // set_global_default(subscriber.with(JsonStorageLayer).with(formatting_layer)).
-    // map_err(|e| format!("{:?}", e))?; } else {
-    //     let _ = set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
-    // }
+      .with_ansi(true)
+      .with_target(false)
+      .with_max_level(tracing::Level::TRACE)
+      .with_writer(std::io::stderr)
+      .with_thread_ids(true)
+      .json()
+      // .with_current_span(true)
+      // .with_span_list(true)
+      .compact()
+      .finish()
+      .with(env_filter)
+      .with(JsonStorageLayer)
+      .with(FlowyFormattingLayer::new(std::io::stdout))
+      .with(FlowyFormattingLayer::new(non_blocking));
 
     set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
     LogTracer::builder()