Browse Source

Merge pull request #978 from AppFlowy-IO/feat/support_group_checkbox

chore: support group by checkbox field
Nathan.fooo 2 years ago
parent
commit
d9aba78727

+ 62 - 27
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -1,4 +1,6 @@
 import 'dart:async';
+import 'dart:collection';
+
 import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
@@ -12,7 +14,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'dart:collection';
 
 import 'board_data_controller.dart';
 import 'group_controller.dart';
@@ -164,12 +165,17 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
     boardController.clear();
 
     //
-    List<AFBoardColumnData> columns = groups.map((group) {
+    List<AFBoardColumnData> columns = groups
+        .where((group) => fieldController.getField(group.fieldId) != null)
+        .map((group) {
       return AFBoardColumnData(
         id: group.groupId,
         name: group.desc,
         items: _buildRows(group),
-        customData: group,
+        customData: BoardCustomData(
+          group: group,
+          fieldContext: fieldController.getField(group.fieldId)!,
+        ),
       );
     }).toList();
     boardController.addColumns(columns);
@@ -177,6 +183,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
     for (final group in groups) {
       final delegate = GroupControllerDelegateImpl(
         controller: boardController,
+        fieldController: fieldController,
         onNewColumnItem: (groupId, row, index) {
           add(BoardEvent.didCreateRow(groupId, row, index));
         },
@@ -238,10 +245,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
 
   List<AFColumnItem> _buildRows(GroupPB group) {
     final items = group.rows.map((row) {
-      return BoardColumnItem(
-        row: row,
-        fieldId: group.fieldId,
-      );
+      final fieldContext = fieldController.getField(group.fieldId);
+      return BoardColumnItem(row: row, fieldContext: fieldContext!);
     }).toList();
 
     return <AFColumnItem>[...items];
@@ -332,15 +337,11 @@ class GridFieldEquatable extends Equatable {
 
 class BoardColumnItem extends AFColumnItem {
   final RowPB row;
-
-  final String fieldId;
-
-  final bool requestFocus;
+  final GridFieldContext fieldContext;
 
   BoardColumnItem({
     required this.row,
-    required this.fieldId,
-    this.requestFocus = false,
+    required this.fieldContext,
   });
 
   @override
@@ -348,24 +349,29 @@ class BoardColumnItem extends AFColumnItem {
 }
 
 class GroupControllerDelegateImpl extends GroupControllerDelegate {
+  final GridFieldController fieldController;
   final AFBoardDataController controller;
   final void Function(String, RowPB, int?) onNewColumnItem;
 
   GroupControllerDelegateImpl({
     required this.controller,
+    required this.fieldController,
     required this.onNewColumnItem,
   });
 
   @override
   void insertRow(GroupPB group, RowPB row, int? index) {
+    final fieldContext = fieldController.getField(group.fieldId);
+    if (fieldContext == null) {
+      Log.warn("FieldContext should not be null");
+      return;
+    }
+
     if (index != null) {
-      final item = BoardColumnItem(row: row, fieldId: group.fieldId);
+      final item = BoardColumnItem(row: row, fieldContext: fieldContext);
       controller.insertColumnItem(group.groupId, index, item);
     } else {
-      final item = BoardColumnItem(
-        row: row,
-        fieldId: group.fieldId,
-      );
+      final item = BoardColumnItem(row: row, fieldContext: fieldContext);
       controller.addColumnItem(group.groupId, item);
     }
   }
@@ -377,22 +383,25 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
 
   @override
   void updateRow(GroupPB group, RowPB row) {
+    final fieldContext = fieldController.getField(group.fieldId);
+    if (fieldContext == null) {
+      Log.warn("FieldContext should not be null");
+      return;
+    }
     controller.updateColumnItem(
       group.groupId,
-      BoardColumnItem(
-        row: row,
-        fieldId: group.fieldId,
-      ),
+      BoardColumnItem(row: row, fieldContext: fieldContext),
     );
   }
 
   @override
   void addNewRow(GroupPB group, RowPB row, int? index) {
-    final item = BoardColumnItem(
-      row: row,
-      fieldId: group.fieldId,
-      requestFocus: true,
-    );
+    final fieldContext = fieldController.getField(group.fieldId);
+    if (fieldContext == null) {
+      Log.warn("FieldContext should not be null");
+      return;
+    }
+    final item = BoardColumnItem(row: row, fieldContext: fieldContext);
 
     if (index != null) {
       controller.insertColumnItem(group.groupId, index, item);
@@ -414,3 +423,29 @@ class BoardEditingRow {
     required this.index,
   });
 }
+
+class BoardCustomData {
+  final GroupPB group;
+  final GridFieldContext fieldContext;
+  BoardCustomData({
+    required this.group,
+    required this.fieldContext,
+  });
+
+  CheckboxGroup? asCheckboxGroup() {
+    if (fieldType != FieldType.Checkbox) return null;
+    return CheckboxGroup(group);
+  }
+
+  FieldType get fieldType => fieldContext.fieldType;
+}
+
+class CheckboxGroup {
+  final GroupPB group;
+
+  CheckboxGroup(this.group);
+
+// Hardcode value: "Yes" that equal to the value defined in Rust
+// pub const CHECK: &str = "Yes";
+  bool get isCheck => group.groupId == "Yes";
+}

+ 70 - 32
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -17,7 +17,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/group.pbserver.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import '../../grid/application/row/row_cache.dart';
@@ -36,8 +36,7 @@ class BoardPage extends StatelessWidget {
       create: (context) =>
           BoardBloc(view: view)..add(const BoardEvent.initial()),
       child: BlocBuilder<BoardBloc, BoardState>(
-        buildWhen: (previous, current) =>
-            previous.loadingState != current.loadingState,
+        buildWhen: (p, c) => p.loadingState != c.loadingState,
         builder: (context, state) {
           return state.loadingState.map(
             loading: (_) =>
@@ -84,36 +83,15 @@ class _BoardContentState extends State<BoardContent> {
       child: BlocBuilder<BoardBloc, BoardState>(
         buildWhen: (previous, current) => previous.groupIds != current.groupIds,
         builder: (context, state) {
-          final theme = context.read<AppTheme>();
+          final column = Column(
+            children: [const _ToolbarBlocAdaptor(), _buildBoard(context)],
+          );
+
           return Container(
-            color: theme.surface,
+            color: context.read<AppTheme>().surface,
             child: Padding(
               padding: const EdgeInsets.symmetric(horizontal: 20),
-              child: Column(
-                children: [
-                  const _ToolbarBlocAdaptor(),
-                  Expanded(
-                    child: AFBoard(
-                      key: UniqueKey(),
-                      scrollManager: scrollManager,
-                      scrollController: scrollController,
-                      dataController: context.read<BoardBloc>().boardController,
-                      headerBuilder: _buildHeader,
-                      footBuilder: _buildFooter,
-                      cardBuilder: (_, column, columnItem) => _buildCard(
-                        context,
-                        column,
-                        columnItem,
-                      ),
-                      columnConstraints:
-                          const BoxConstraints.tightFor(width: 300),
-                      config: AFBoardConfig(
-                        columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
-                      ),
-                    ),
-                  ),
-                ],
-              ),
+              child: column,
             ),
           );
         },
@@ -121,6 +99,27 @@ class _BoardContentState extends State<BoardContent> {
     );
   }
 
+  Expanded _buildBoard(BuildContext context) {
+    return Expanded(
+      child: AFBoard(
+        scrollManager: scrollManager,
+        scrollController: scrollController,
+        dataController: context.read<BoardBloc>().boardController,
+        headerBuilder: _buildHeader,
+        footBuilder: _buildFooter,
+        cardBuilder: (_, column, columnItem) => _buildCard(
+          context,
+          column,
+          columnItem,
+        ),
+        columnConstraints: const BoxConstraints.tightFor(width: 300),
+        config: AFBoardConfig(
+          columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
+        ),
+      ),
+    );
+  }
+
   void _handleEditState(BoardState state, BuildContext context) {
     state.editingRow.fold(
       () => null,
@@ -152,6 +151,7 @@ class _BoardContentState extends State<BoardContent> {
     BuildContext context,
     AFBoardColumnData columnData,
   ) {
+    final boardCustomData = columnData.customData as BoardCustomData;
     return AppFlowyColumnHeader(
       title: Flexible(
         fit: FlexFit.tight,
@@ -162,6 +162,7 @@ class _BoardContentState extends State<BoardContent> {
           color: context.read<AppTheme>().textColor,
         ),
       ),
+      icon: _buildHeaderIcon(boardCustomData),
       addIcon: SizedBox(
         height: 20,
         width: 20,
@@ -181,7 +182,9 @@ class _BoardContentState extends State<BoardContent> {
   }
 
   Widget _buildFooter(BuildContext context, AFBoardColumnData columnData) {
-    final group = columnData.customData as GroupPB;
+    final boardCustomData = columnData.customData as BoardCustomData;
+    final group = boardCustomData.group;
+
     if (group.isDefault) {
       return const SizedBox();
     } else {
@@ -246,7 +249,7 @@ class _BoardContentState extends State<BoardContent> {
       child: BoardCard(
         gridId: gridId,
         groupId: column.id,
-        fieldId: boardColumnItem.fieldId,
+        fieldId: boardColumnItem.fieldContext.id,
         isEditing: isEditing,
         cellBuilder: cellBuilder,
         dataController: cardController,
@@ -319,3 +322,38 @@ extension HexColor on Color {
     return Color(int.parse(buffer.toString(), radix: 16));
   }
 }
+
+Widget? _buildHeaderIcon(BoardCustomData customData) {
+  Widget? widget;
+  switch (customData.fieldType) {
+    case FieldType.Checkbox:
+      final group = customData.asCheckboxGroup()!;
+      if (group.isCheck) {
+        widget = svgWidget('editor/editor_check');
+      } else {
+        widget = svgWidget('editor/editor_uncheck');
+      }
+      break;
+    case FieldType.DateTime:
+      break;
+    case FieldType.MultiSelect:
+      break;
+    case FieldType.Number:
+      break;
+    case FieldType.RichText:
+      break;
+    case FieldType.SingleSelect:
+      break;
+    case FieldType.URL:
+      break;
+  }
+
+  if (widget != null) {
+    widget = SizedBox(
+      width: 20,
+      height: 20,
+      child: widget,
+    );
+  }
+  return widget;
+}

+ 22 - 9
frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart

@@ -7,6 +7,7 @@ import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import '../row/row_cache.dart';
@@ -35,12 +36,12 @@ class GridFieldController {
   final SettingListener _settingListener;
   final Map<OnReceiveFields, VoidCallback> _fieldCallbackMap = {};
   final Map<OnChangeset, OnChangeset> _changesetCallbackMap = {};
-
-  _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
-  List<String> _groupFieldIds = [];
   final GridFFIService _gridFFIService;
   final SettingFFIService _settingFFIService;
 
+  _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
+  final Map<String, GridGroupConfigurationPB> _configurationByFieldId = {};
+
   List<GridFieldContext> get fieldContexts =>
       [..._fieldNotifier?.fieldContexts ?? []];
 
@@ -67,31 +68,43 @@ class GridFieldController {
     //Listen on setting changes
     _settingListener.start(onSettingUpdated: (result) {
       result.fold(
-        (setting) => _updateFieldsWhenSettingChanged(setting),
+        (setting) => _updateGroupConfiguration(setting),
         (r) => Log.error(r),
       );
     });
 
     _settingFFIService.getSetting().then((result) {
       result.fold(
-        (setting) => _updateFieldsWhenSettingChanged(setting),
+        (setting) => _updateGroupConfiguration(setting),
         (err) => Log.error(err),
       );
     });
   }
 
-  void _updateFieldsWhenSettingChanged(GridSettingPB setting) {
-    _groupFieldIds = setting.groupConfigurations.items
-        .map((item) => item.groupFieldId)
+  GridFieldContext? getField(String fieldId) {
+    final fields = _fieldNotifier?.fieldContexts
+        .where(
+          (element) => element.id == fieldId,
+        )
         .toList();
+    if (fields?.isEmpty ?? true) {
+      return null;
+    }
+    return fields!.first;
+  }
 
+  void _updateGroupConfiguration(GridSettingPB setting) {
+    _configurationByFieldId.clear();
+    for (final configuration in setting.groupConfigurations.items) {
+      _configurationByFieldId[configuration.fieldId] = configuration;
+    }
     _updateFieldContexts();
   }
 
   void _updateFieldContexts() {
     if (_fieldNotifier != null) {
       for (var field in _fieldNotifier!.fieldContexts) {
-        if (_groupFieldIds.contains(field.id)) {
+        if (_configurationByFieldId[field.id] != null) {
           field._isGroupField = true;
         } else {
           field._isGroupField = false;

+ 1 - 1
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart

@@ -31,7 +31,7 @@ typedef AFBoardColumnCardBuilder = Widget Function(
 
 typedef AFBoardColumnHeaderBuilder = Widget? Function(
   BuildContext context,
-  AFBoardColumnData headerData,
+  AFBoardColumnData columnData,
 );
 
 typedef AFBoardColumnFooterBuilder = Widget Function(

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

@@ -44,14 +44,14 @@ pub struct GridGroupConfigurationPB {
     pub id: String,
 
     #[pb(index = 2)]
-    pub group_field_id: String,
+    pub field_id: String,
 }
 
 impl std::convert::From<&GroupConfigurationRevision> for GridGroupConfigurationPB {
     fn from(rev: &GroupConfigurationRevision) -> Self {
         GridGroupConfigurationPB {
             id: rev.id.clone(),
-            group_field_id: rev.field_id.clone(),
+            field_id: rev.field_id.clone(),
         }
     }
 }

+ 0 - 1
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -182,7 +182,6 @@ pub fn delete_select_option_cell(option_id: String, field_rev: &FieldRevision) -
     CellRevision::new(data)
 }
 
-/// If the cell data is not String type, it should impl this trait.
 /// Deserialize the String into cell specific data type.  
 pub trait FromCellString {
     fn from_cell_str(s: &str) -> FlowyResult<Self>

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

@@ -1,13 +1,16 @@
 use crate::entities::GroupChangesetPB;
 
 use crate::services::group::controller::MoveGroupRowContext;
-use flowy_grid_data_model::revision::RowRevision;
+use flowy_grid_data_model::revision::{CellRevision, RowRevision};
 
 pub trait GroupAction: Send + Sync {
     type CellDataType;
+    fn default_cell_rev(&self) -> Option<CellRevision> {
+        None
+    }
+
     fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
     fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>;
     fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>;
-
     fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec<GroupChangesetPB>;
 }

+ 7 - 3
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -7,7 +7,6 @@ use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
     FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer,
 };
-
 use std::marker::PhantomData;
 use std::sync::Arc;
 
@@ -193,9 +192,14 @@ where
     #[tracing::instrument(level = "trace", skip_all, fields(row_count=%row_revs.len(), group_result))]
     fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
         for row_rev in row_revs {
-            if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
+            let cell_rev = match row_rev.cells.get(&self.field_id) {
+                None => self.default_cell_rev(),
+                Some(cell_rev) => Some(cell_rev.clone()),
+            };
+
+            if let Some(cell_rev) = cell_rev {
                 let mut grouped_rows: Vec<GroupedRow> = vec![];
-                let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
+                let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev);
                 let cell_data = cell_bytes.parser::<P>()?;
                 for group in self.group_ctx.concrete_groups() {
                     if self.can_group(&group.filter_content, &cell_data) {

+ 73 - 21
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs

@@ -1,4 +1,4 @@
-use crate::entities::GroupChangesetPB;
+use crate::entities::{GroupChangesetPB, InsertedRowPB, RowPB};
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
 use crate::services::group::action::GroupAction;
 use crate::services::group::configuration::GroupContext;
@@ -6,8 +6,11 @@ use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
 
-use crate::services::group::GeneratedGroup;
-use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision};
+use crate::services::cell::insert_checkbox_cell;
+use crate::services::group::{move_group_row, GeneratedGroup};
+use flowy_grid_data_model::revision::{
+    CellRevision, CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision,
+};
 
 pub type CheckboxGroupController = GenericGroupController<
     CheckboxGroupConfigurationRevision,
@@ -20,30 +23,79 @@ pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision>
 
 impl GroupAction for CheckboxGroupController {
     type CellDataType = CheckboxCellData;
-    fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
-        false
+    fn default_cell_rev(&self) -> Option<CellRevision> {
+        Some(CellRevision::new(UNCHECK.to_string()))
     }
 
-    fn add_row_if_match(&mut self, _row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
-        todo!()
+    fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool {
+        return if cell_data.is_check() {
+            content == CHECK
+        } else {
+            content == UNCHECK
+        };
     }
 
-    fn remove_row_if_match(
-        &mut self,
-        _row_rev: &RowRevision,
-        _cell_data: &Self::CellDataType,
-    ) -> Vec<GroupChangesetPB> {
-        todo!()
+    fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+        let mut changesets = vec![];
+        self.group_ctx.iter_mut_groups(|group| {
+            let mut changeset = GroupChangesetPB::new(group.id.clone());
+            let is_contained = group.contains_row(&row_rev.id);
+            if group.id == CHECK && cell_data.is_check() {
+                if !is_contained {
+                    let row_pb = RowPB::from(row_rev);
+                    changeset.inserted_rows.push(InsertedRowPB::new(row_pb.clone()));
+                    group.add_row(row_pb);
+                }
+            } else {
+                if is_contained {
+                    changeset.deleted_rows.push(row_rev.id.clone());
+                    group.remove_row(&row_rev.id);
+                }
+            }
+            if !changeset.is_empty() {
+                changesets.push(changeset);
+            }
+        });
+        changesets
+    }
+
+    fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+        let mut changesets = vec![];
+        self.group_ctx.iter_mut_groups(|group| {
+            let mut changeset = GroupChangesetPB::new(group.id.clone());
+            if group.contains_row(&row_rev.id) {
+                changeset.deleted_rows.push(row_rev.id.clone());
+                group.remove_row(&row_rev.id);
+            }
+
+            if !changeset.is_empty() {
+                changesets.push(changeset);
+            }
+        });
+        changesets
     }
 
-    fn move_row(&mut self, _cell_data: &Self::CellDataType, _context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
-        todo!()
+    fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
+        let mut group_changeset = vec![];
+        self.group_ctx.iter_mut_groups(|group| {
+            if let Some(changeset) = move_group_row(group, &mut context) {
+                group_changeset.push(changeset);
+            }
+        });
+        group_changeset
     }
 }
 
 impl GroupController for CheckboxGroupController {
-    fn will_create_row(&mut self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {
-        todo!()
+    fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
+        match self.group_ctx.get_group(group_id) {
+            None => tracing::warn!("Can not find the group: {}", group_id),
+            Some((_, group)) => {
+                let is_check = group.id == CHECK;
+                let cell_rev = insert_checkbox_cell(is_check, field_rev);
+                row_rev.cells.insert(field_rev.id.clone(), cell_rev);
+            }
+        }
     }
 }
 
@@ -58,13 +110,13 @@ impl GroupGenerator for CheckboxGroupGenerator {
         _type_option: &Option<Self::TypeOptionType>,
     ) -> Vec<GeneratedGroup> {
         let check_group = GeneratedGroup {
-            group_rev: GroupRevision::new("true".to_string(), CHECK.to_string()),
-            filter_content: "".to_string(),
+            group_rev: GroupRevision::new(CHECK.to_string(), "".to_string()),
+            filter_content: CHECK.to_string(),
         };
 
         let uncheck_group = GeneratedGroup {
-            group_rev: GroupRevision::new("false".to_string(), UNCHECK.to_string()),
-            filter_content: "".to_string(),
+            group_rev: GroupRevision::new(UNCHECK.to_string(), "".to_string()),
+            filter_content: UNCHECK.to_string(),
         };
         vec![check_group, uncheck_group]
     }

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

@@ -46,10 +46,10 @@ impl GroupAction for MultiSelectGroupController {
         changesets
     }
 
-    fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
+    fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
         self.group_ctx.iter_mut_groups(|group| {
-            if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) {
+            if let Some(changeset) = move_group_row(group, &mut context) {
                 group_changeset.push(changeset);
             }
         });

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

@@ -46,10 +46,10 @@ impl GroupAction for SingleSelectGroupController {
         changesets
     }
 
-    fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
+    fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
         self.group_ctx.iter_mut_groups(|group| {
-            if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) {
+            if let Some(changeset) = move_group_row(group, &mut context) {
                 group_changeset.push(changeset);
             }
         });

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

@@ -62,11 +62,7 @@ pub fn remove_select_option_row(
     }
 }
 
-pub fn move_select_option_row(
-    group: &mut Group,
-    _cell_data: &SelectOptionCellDataPB,
-    context: &mut MoveGroupRowContext,
-) -> Option<GroupChangesetPB> {
+pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> Option<GroupChangesetPB> {
     let mut changeset = GroupChangesetPB::new(group.id.clone());
     let MoveGroupRowContext {
         row_rev,