feat: enable group condition (#5248)

* feat: enable group condition

* style: added i18n for date field group conditions

* fix: flutter analyze

* fix: test use i18n

* fix: more localization

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
Co-authored-by: Mathias Mogensen <mathias@appflowy.io>
This commit is contained in:
Mohammad Zolfaghari 2024-06-13 01:32:16 +03:30 committed by GitHub
parent f1a4deb459
commit 4f4be7eac7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 421 additions and 70 deletions

View File

@ -137,6 +137,8 @@ class FieldController {
List<FieldInfo> get fieldInfos => [..._fieldNotifier.fieldInfos];
List<FilterInfo> get filterInfos => [..._filterNotifier?.filters ?? []];
List<SortInfo> get sortInfos => [..._sortNotifier?.sorts ?? []];
List<GroupSettingPB> get groupSettings =>
_groupConfigurationByFieldId.entries.map((e) => e.value).toList();
FieldInfo? getField(String fieldId) {
return _fieldNotifier.fieldInfos

View File

@ -1,5 +1,6 @@
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart';
part 'field_info.freezed.dart';
@ -89,4 +90,13 @@ class FieldInfo with _$FieldInfo {
return false;
}
}
List<ProtobufEnum> get groupConditions {
switch (field.fieldType) {
case FieldType.DateTime:
return DateConditionPB.values;
default:
return [];
}
}
}

View File

@ -6,6 +6,7 @@ import 'package:appflowy/plugins/database/domain/group_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -22,6 +23,7 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
viewId,
databaseController.fieldController.fieldInfos,
databaseController.databaseLayoutSetting!.board,
databaseController.fieldController.groupSettings,
),
) {
_dispatch();
@ -51,11 +53,22 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
_startListening();
},
didReceiveFieldUpdate: (fieldInfos) {
emit(state.copyWith(fieldInfos: fieldInfos));
emit(
state.copyWith(
fieldInfos: fieldInfos,
groupSettings:
_databaseController.fieldController.groupSettings,
),
);
},
setGroupByField: (String fieldId, FieldType fieldType) async {
setGroupByField: (
String fieldId,
FieldType fieldType, [
List<int>? settingContent,
]) async {
final result = await _groupBackendSvc.groupByField(
fieldId: fieldId,
settingContent: settingContent ?? [],
);
result.fold((l) => null, (err) => Log.error(err));
},
@ -96,8 +109,9 @@ class DatabaseGroupEvent with _$DatabaseGroupEvent {
const factory DatabaseGroupEvent.initial() = _Initial;
const factory DatabaseGroupEvent.setGroupByField(
String fieldId,
FieldType fieldType,
) = _DatabaseGroupEvent;
FieldType fieldType, [
@Default([]) List<int> settingContent,
]) = _DatabaseGroupEvent;
const factory DatabaseGroupEvent.didReceiveFieldUpdate(
List<FieldInfo> fields,
) = _DidReceiveFieldUpdate;
@ -112,16 +126,19 @@ class DatabaseGroupState with _$DatabaseGroupState {
required String viewId,
required List<FieldInfo> fieldInfos,
required BoardLayoutSettingPB layoutSettings,
required List<GroupSettingPB> groupSettings,
}) = _DatabaseGroupState;
factory DatabaseGroupState.initial(
String viewId,
List<FieldInfo> fieldInfos,
BoardLayoutSettingPB layoutSettings,
List<GroupSettingPB> groupSettings,
) =>
DatabaseGroupState(
viewId: viewId,
fieldInfos: fieldInfos,
layoutSettings: layoutSettings,
groupSettings: groupSettings,
);
}

View File

@ -16,8 +16,10 @@ import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:intl/intl.dart';
import 'package:protobuf/protobuf.dart' hide FieldInfo;
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:calendar_view/calendar_view.dart';
import '../../application/database_controller.dart';
import '../../application/field/field_controller.dart';
@ -527,6 +529,9 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
return "No ${field.name}";
}
final groupSettings = databaseController.fieldController.groupSettings
.firstWhereOrNull((gs) => gs.fieldId == field.id);
switch (field.fieldType) {
case FieldType.SingleSelect:
final options =
@ -547,33 +552,61 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
case FieldType.URL:
return group.groupId;
case FieldType.DateTime:
// Assume DateCondition::Relative as there isn't an option for this
// right now.
final config = groupSettings?.content != null
? DateGroupConfigurationPB.fromBuffer(groupSettings!.content)
: DateGroupConfigurationPB();
final dateFormat = DateFormat("y/MM/dd");
try {
final targetDateTime = dateFormat.parseLoose(group.groupId);
final targetDateTimeDay = DateTime(
targetDateTime.year,
targetDateTime.month,
targetDateTime.day,
);
final now = DateTime.now();
final nowDay = DateTime(
now.year,
now.month,
now.day,
);
final diff = targetDateTimeDay.difference(nowDay).inDays;
return switch (diff) {
0 => "Today",
-1 => "Yesterday",
1 => "Tomorrow",
-7 => "Last 7 days",
2 => "Next 7 days",
-30 => "Last 30 days",
8 => "Next 30 days",
_ => DateFormat("MMM y").format(targetDateTimeDay)
};
switch (config.condition) {
case DateConditionPB.Day:
return DateFormat("MMM dd, y").format(targetDateTime);
case DateConditionPB.Week:
final beginningOfWeek = targetDateTime
.subtract(Duration(days: targetDateTime.weekday - 1));
final endOfWeek = targetDateTime.add(
Duration(days: DateTime.daysPerWeek - targetDateTime.weekday),
);
final beginningOfWeekFormat =
beginningOfWeek.year != endOfWeek.year
? "MMM dd y"
: "MMM dd";
final endOfWeekFormat = beginningOfWeek.month != endOfWeek.month
? "MMM dd y"
: "dd y";
return LocaleKeys.board_dateCondition_weekOf.tr(
args: [
DateFormat(beginningOfWeekFormat).format(beginningOfWeek),
DateFormat(endOfWeekFormat).format(endOfWeek),
],
);
case DateConditionPB.Month:
return DateFormat("MMM y").format(targetDateTime);
case DateConditionPB.Year:
return DateFormat("y").format(targetDateTime);
case DateConditionPB.Relative:
final targetDateTimeDay = DateTime(
targetDateTime.year,
targetDateTime.month,
targetDateTime.day,
);
final nowDay = DateTime.now().withoutTime;
final diff = targetDateTimeDay.difference(nowDay).inDays;
return switch (diff) {
0 => LocaleKeys.board_dateCondition_today.tr(),
-1 => LocaleKeys.board_dateCondition_yesterday.tr(),
1 => LocaleKeys.board_dateCondition_tomorrow.tr(),
-7 => LocaleKeys.board_dateCondition_lastSevenDays.tr(),
2 => LocaleKeys.board_dateCondition_nextSevenDays.tr(),
-30 => LocaleKeys.board_dateCondition_lastThirtyDays.tr(),
8 => LocaleKeys.board_dateCondition_nextThirtyDays.tr(),
_ => DateFormat("MMM y").format(targetDateTimeDay)
};
default:
return "";
}
} on FormatException {
return "";
}

View File

@ -1,5 +1,5 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_result/appflowy_result.dart';
@ -10,10 +10,12 @@ class GroupBackendService {
Future<FlowyResult<void, FlowyError>> groupByField({
required String fieldId,
required List<int> settingContent,
}) {
final payload = GroupByFieldPayloadPB.create()
..viewId = viewId
..fieldId = fieldId;
..fieldId = fieldId
..settingContent = settingContent;
return DatabaseEventSetGroupByField(payload).send();
}

View File

@ -8,8 +8,7 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_
import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
@ -17,6 +16,7 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:protobuf/protobuf.dart' hide FieldInfo;
@ -42,12 +42,21 @@ class DatabaseGroupList extends StatelessWidget {
)..add(const DatabaseGroupEvent.initial()),
child: BlocBuilder<DatabaseGroupBloc, DatabaseGroupState>(
builder: (context, state) {
final showHideUngroupedToggle = state.fieldInfos.any(
(field) =>
field.canBeGroup &&
field.isGroupField &&
field.fieldType != FieldType.Checkbox,
final field = state.fieldInfos.firstWhereOrNull(
(field) => field.canBeGroup && field.isGroupField,
);
final showHideUngroupedToggle =
field?.fieldType != FieldType.Checkbox;
DateGroupConfigurationPB? config;
if (field != null) {
final gs = state.groupSettings
.firstWhereOrNull((gs) => gs.fieldId == field.id);
config = gs != null
? DateGroupConfigurationPB.fromBuffer(gs.content)
: null;
}
final children = [
if (showHideUngroupedToggle) ...[
SizedBox(
@ -90,10 +99,37 @@ class DatabaseGroupList extends StatelessWidget {
...state.fieldInfos.where((fieldInfo) => fieldInfo.canBeGroup).map(
(fieldInfo) => _GridGroupCell(
fieldInfo: fieldInfo,
name: fieldInfo.name,
icon: fieldInfo.fieldType.svgData,
checked: fieldInfo.isGroupField,
onSelected: onDismissed,
key: ValueKey(fieldInfo.id),
),
),
if (field?.groupConditions.isNotEmpty ?? false) ...[
const TypeOptionSeparator(spacing: 0),
SizedBox(
height: GridSize.popoverItemHeight,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: FlowyText.medium(
LocaleKeys.board_groupCondition.tr(),
textAlign: TextAlign.left,
color: Theme.of(context).hintColor,
),
),
),
...field!.groupConditions.map(
(condition) => _GridGroupCell(
fieldInfo: field,
name: condition.name,
condition: condition.value,
onSelected: onDismissed,
checked: config?.condition == condition,
),
),
],
];
return ListView.separated(
@ -128,15 +164,23 @@ class _GridGroupCell extends StatelessWidget {
super.key,
required this.fieldInfo,
required this.onSelected,
required this.checked,
required this.name,
this.condition = 0,
this.icon,
});
final FieldInfo fieldInfo;
final VoidCallback onSelected;
final bool checked;
final int condition;
final String name;
final FlowySvgData? icon;
@override
Widget build(BuildContext context) {
Widget? rightIcon;
if (fieldInfo.isGroupField) {
if (checked) {
rightIcon = const Padding(
padding: EdgeInsets.all(2.0),
child: FlowySvg(FlowySvgs.check_s),
@ -150,19 +194,31 @@ class _GridGroupCell extends StatelessWidget {
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
fieldInfo.name,
name,
color: AFThemeExtension.of(context).textColor,
),
leftIcon: FlowySvg(
fieldInfo.fieldType.svgData,
color: Theme.of(context).iconTheme.color,
),
leftIcon: icon != null
? FlowySvg(
icon!,
color: Theme.of(context).iconTheme.color,
)
: null,
rightIcon: rightIcon,
onTap: () {
List<int> settingContent = [];
switch (fieldInfo.fieldType) {
case FieldType.DateTime:
final config = DateGroupConfigurationPB()
..condition = DateConditionPB.values[condition];
settingContent = config.writeToBuffer();
break;
default:
}
context.read<DatabaseGroupBloc>().add(
DatabaseGroupEvent.setGroupByField(
fieldInfo.id,
fieldInfo.fieldType,
settingContent,
),
);
onSelected();

View File

@ -0,0 +1,115 @@
import 'package:appflowy/plugins/database/application/cell/bloc/date_cell_editor_bloc.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/plugins/database/application/setting/group_bloc.dart';
import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'util.dart';
void main() {
late AppFlowyBoardTest boardTest;
setUpAll(() async {
boardTest = await AppFlowyBoardTest.ensureInitialized();
});
test('group by date field test', () async {
final context = await boardTest.createTestBoard();
final boardBloc = BoardBloc(
databaseController: DatabaseController(view: context.gridView),
)..add(const BoardEvent.initial());
await boardResponseFuture();
// assert the initial values
assert(boardBloc.groupControllers.values.length == 4);
assert(context.fieldContexts.length == 2);
await context.createField(FieldType.DateTime);
await boardResponseFuture();
assert(context.fieldContexts.length == 3);
final dateField = context.fieldContexts.last.field;
final cellController = context.makeCellControllerFromFieldId(dateField.id)
as DateCellController;
final bloc = DateCellEditorBloc(
cellController: cellController,
reminderBloc: getIt<ReminderBloc>(),
);
await boardResponseFuture();
bloc.add(DateCellEditorEvent.selectDay(DateTime.now()));
await boardResponseFuture();
final gridGroupBloc = DatabaseGroupBloc(
viewId: context.gridView.id,
databaseController: context.databaseController,
)..add(const DatabaseGroupEvent.initial());
gridGroupBloc.add(
DatabaseGroupEvent.setGroupByField(
dateField.id,
dateField.fieldType,
),
);
await boardResponseFuture();
assert(boardBloc.groupControllers.values.length == 2);
assert(
boardBloc.boardController.groupDatas.last.headerData.groupName ==
LocaleKeys.board_dateCondition_today.tr(),
);
});
test('group by date field with condition', () async {
final context = await boardTest.createTestBoard();
final boardBloc = BoardBloc(
databaseController: DatabaseController(view: context.gridView),
)..add(const BoardEvent.initial());
await boardResponseFuture();
// assert the initial values
assert(boardBloc.groupControllers.values.length == 4);
assert(context.fieldContexts.length == 2);
await context.createField(FieldType.DateTime);
await boardResponseFuture();
assert(context.fieldContexts.length == 3);
final dateField = context.fieldContexts.last.field;
final cellController = context.makeCellControllerFromFieldId(dateField.id)
as DateCellController;
final bloc = DateCellEditorBloc(
cellController: cellController,
reminderBloc: getIt<ReminderBloc>(),
);
await boardResponseFuture();
bloc.add(DateCellEditorEvent.selectDay(DateTime.now()));
await boardResponseFuture();
final gridGroupBloc = DatabaseGroupBloc(
viewId: context.gridView.id,
databaseController: context.databaseController,
)..add(const DatabaseGroupEvent.initial());
final settingContent = DateGroupConfigurationPB()
..condition = DateConditionPB.Year;
gridGroupBloc.add(
DatabaseGroupEvent.setGroupByField(
dateField.id,
dateField.fieldType,
settingContent.writeToBuffer(),
),
);
await boardResponseFuture();
assert(boardBloc.groupControllers.values.length == 2);
assert(
boardBloc.boardController.groupDatas.last.headerData.groupName == "2024",
);
});
}

View File

@ -1436,6 +1436,7 @@
"ungroupedButtonTooltip": "Contains cards that don't belong in any group",
"ungroupedItemsTitle": "Click to add to the board",
"groupBy": "Group by",
"groupCondition": "Group condition",
"referencedBoardPrefix": "View of",
"notesTooltip": "Notes inside",
"mobile": {
@ -1444,6 +1445,16 @@
"showGroupContent": "Are you sure you want to show this group on the board?",
"failedToLoad": "Failed to load board view"
},
"dateCondition": {
"weekOf": "Week of {} - {}",
"today": "Today",
"yesterday": "Yesterday",
"tomorrow": "Tomorrow",
"lastSevenDays": "Last 7 days",
"nextSevenDays": "Next 7 days",
"lastThirtyDays": "Last 30 days",
"nextThirtyDays": "Next 30 days"
},
"noGroup": "No group by property",
"noGroupDesc": "Board views require a property to group by in order to display"
},

View File

@ -444,12 +444,18 @@ impl EventIntegrationTest {
.error()
}
pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
pub async fn set_group_by_field(
&self,
view_id: &str,
field_id: &str,
setting_content: Vec<u8>,
) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(DatabaseEvent::SetGroupByField)
.payload(GroupByFieldPayloadPB {
field_id: field_id.to_string(),
view_id: view_id.to_string(),
setting_content,
})
.async_send()
.await

View File

@ -697,7 +697,7 @@ async fn update_database_layout_event_test2() {
.find(|field| field.field_type == FieldType::Checkbox)
.unwrap();
test
.set_group_by_field(&grid_view.id, &checkbox_field.id)
.set_group_by_field(&grid_view.id, &checkbox_field.id, vec![])
.await;
let error = test
@ -725,7 +725,7 @@ async fn set_group_by_checkbox_field_test() {
let checkbox_field = test.create_field(&board_view.id, FieldType::Checkbox).await;
test
.set_group_by_field(&board_view.id, &checkbox_field.id)
.set_group_by_field(&board_view.id, &checkbox_field.id, vec![])
.await;
let groups = test.get_groups(&board_view.id).await;

View File

@ -1,5 +1,10 @@
use crate::services::group::Group;
use crate::{
entities::FieldType,
services::group::{DateCondition, DateGroupConfiguration, Group},
};
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyResult;
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct URLGroupConfigurationPB {
@ -46,16 +51,33 @@ pub struct NumberGroupConfigurationPB {
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct DateGroupConfigurationPB {
#[pb(index = 1)]
pub condition: DateCondition,
pub condition: DateConditionPB,
#[pb(index = 2)]
hide_empty: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
impl From<DateGroupConfigurationPB> for DateGroupConfiguration {
fn from(data: DateGroupConfigurationPB) -> Self {
Self {
condition: data.condition.into(),
hide_empty: data.hide_empty,
}
}
}
impl From<DateGroupConfiguration> for DateGroupConfigurationPB {
fn from(data: DateGroupConfiguration) -> Self {
Self {
condition: data.condition.into(),
hide_empty: data.hide_empty,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, Default)]
#[repr(u8)]
#[derive(Default)]
pub enum DateCondition {
pub enum DateConditionPB {
#[default]
Relative = 0,
Day = 1,
@ -64,8 +86,56 @@ pub enum DateCondition {
Year = 4,
}
impl From<DateConditionPB> for DateCondition {
fn from(data: DateConditionPB) -> Self {
match data {
DateConditionPB::Relative => DateCondition::Relative,
DateConditionPB::Day => DateCondition::Day,
DateConditionPB::Week => DateCondition::Week,
DateConditionPB::Month => DateCondition::Month,
DateConditionPB::Year => DateCondition::Year,
}
}
}
impl From<DateCondition> for DateConditionPB {
fn from(data: DateCondition) -> Self {
match data {
DateCondition::Relative => DateConditionPB::Relative,
DateCondition::Day => DateConditionPB::Day,
DateCondition::Week => DateConditionPB::Week,
DateCondition::Month => DateConditionPB::Month,
DateCondition::Year => DateConditionPB::Year,
}
}
}
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct CheckboxGroupConfigurationPB {
#[pb(index = 1)]
pub(crate) hide_empty: bool,
}
pub fn group_config_pb_to_json_str<T: Into<Bytes>>(
bytes: T,
field_type: &FieldType,
) -> FlowyResult<String> {
let bytes = bytes.into();
match field_type {
FieldType::DateTime => DateGroupConfigurationPB::try_from(bytes)
.map(|pb| DateGroupConfiguration::from(pb).to_json())?,
_ => Ok("".to_string()),
}
}
pub fn group_config_json_to_pb(setting_content: String, field_type: &FieldType) -> Bytes {
match field_type {
FieldType::DateTime => {
let date_group_config = DateGroupConfiguration::from_json(setting_content.as_ref()).unwrap();
DateGroupConfigurationPB::from(date_group_config)
.try_into()
.unwrap()
},
_ => Bytes::new(),
}
}

View File

@ -5,9 +5,11 @@ use flowy_error::ErrorCode;
use validator::Validate;
use crate::entities::parser::NotEmptyStr;
use crate::entities::RowMetaPB;
use crate::entities::{FieldType, RowMetaPB};
use crate::services::group::{GroupChangeset, GroupData, GroupSetting};
use super::group_config_json_to_pb;
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct GroupSettingPB {
#[pb(index = 1)]
@ -15,13 +17,18 @@ pub struct GroupSettingPB {
#[pb(index = 2)]
pub field_id: String,
#[pb(index = 3)]
pub content: Vec<u8>,
}
impl std::convert::From<&GroupSetting> for GroupSettingPB {
fn from(rev: &GroupSetting) -> Self {
let field_type = FieldType::from(rev.field_type);
GroupSettingPB {
id: rev.id.clone(),
field_id: rev.field_id.clone(),
content: group_config_json_to_pb(rev.content.clone(), &field_type).to_vec(),
}
}
}
@ -105,6 +112,9 @@ pub struct GroupByFieldPayloadPB {
#[pb(index = 2)]
pub view_id: String,
#[pb(index = 3)]
pub setting_content: Vec<u8>,
}
impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
@ -118,13 +128,18 @@ impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
.map_err(|_| ErrorCode::ViewIdIsInvalid)?
.0;
Ok(GroupByFieldParams { field_id, view_id })
Ok(GroupByFieldParams {
field_id,
view_id,
setting_content: self.setting_content,
})
}
}
pub struct GroupByFieldParams {
pub field_id: String,
pub view_id: String,
pub setting_content: Vec<u8>,
}
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone, Validate)]

View File

@ -656,7 +656,7 @@ pub(crate) async fn set_group_by_field_handler(
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)
.set_group_by_field(&params.view_id, &params.field_id, params.setting_content)
.await?;
Ok(())
}

View File

@ -136,20 +136,37 @@ impl DatabaseEditor {
self.database.lock().fields.get_field(field_id)
}
pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
pub async fn set_group_by_field(
&self,
view_id: &str,
field_id: &str,
data: Vec<u8>,
) -> FlowyResult<()> {
let old_group_settings: Vec<GroupSetting>;
let mut setting_content = "".to_string();
{
let database = self.database.lock();
let field = database.fields.get_field(field_id);
old_group_settings = database.get_all_group_setting(view_id);
if let Some(field) = field {
let group_setting = default_group_setting(&field);
let field_type = FieldType::from(field.field_type);
setting_content = group_config_pb_to_json_str(data, &field_type)?;
let mut group_setting = default_group_setting(&field);
group_setting.content = setting_content.clone();
database.views.update_database_view(view_id, |view| {
view.set_groups(vec![group_setting.into()]);
});
}
}
let old_group_setting = old_group_settings.iter().find(|g| g.field_id == field_id);
let has_same_content =
old_group_setting.is_some() && old_group_setting.unwrap().content == setting_content;
let view_editor = self.database_views.get_view_editor(view_id).await?;
view_editor.v_initialize_new_group(field_id).await?;
if !view_editor.is_grouping_field(field_id).await || !has_same_content {
view_editor.v_initialize_new_group(field_id).await?;
}
Ok(())
}

View File

@ -401,15 +401,12 @@ impl DatabaseViewEditor {
/// Called when the user changes the grouping field
pub async fn v_initialize_new_group(&self, field_id: &str) -> FlowyResult<()> {
let is_grouping_field = self.is_grouping_field(field_id).await;
if !is_grouping_field {
self.v_group_by_field(field_id).await?;
if let Some(view) = self.delegate.get_view(&self.view_id).await {
let setting = database_view_setting_pb_from_view(view);
notify_did_update_setting(&self.view_id, setting).await;
}
if let Some(view) = self.delegate.get_view(&self.view_id).await {
let setting = database_view_setting_pb_from_view(view);
notify_did_update_setting(&self.view_id, setting).await;
}
self.v_group_by_field(field_id).await?;
Ok(())
}

View File

@ -6,7 +6,7 @@ use collab_database::rows::{new_cell_builder, Cell, Cells, Row, RowDetail};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use flowy_error::FlowyResult;
use flowy_error::{internal_error, FlowyResult};
use crate::entities::{
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
@ -27,13 +27,13 @@ pub struct DateGroupConfiguration {
}
impl DateGroupConfiguration {
fn from_json(s: &str) -> Result<Self, serde_json::Error> {
pub fn from_json(s: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(s)
}
#[allow(dead_code)]
fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
pub fn to_json(&self) -> FlowyResult<String> {
serde_json::to_string(self).map_err(internal_error)
}
}