feat: clear date value (#2700)

* chore: Add _ClearDateFieldButton

* chore: Add new DateCellCalendarEvent to clear date

* chore: Add grid_field_clearDate translation token

* refactor: _ClearDateFieldButton -> _ClearDateButton

* fix: Fix dart(unused_element) in _ClearDateButton

* feat: Handle clearDate

* Revert "feat: Handle clearDate"

This reverts commit fd145ef8e9589f3b4aa1a16e5d72c15b6d0b53c4.

* feat: Handle clearDate

* feat: Add clear_flag field

* feat: check clear_flag to clear date

* chore: update exisitng tests

* fix: add mssing import

* refactor: remove check for timeFormatError

* fix: fix margin on _ClearDateButton

* refactor: _ClearDateButton -> ClearDateButton

* test: add clear date backend unit test

* test: add clear date integration test

* test: add clear date to edit time cell test

* chore: mark ClearDateButton visible for testing

* feat: close date editor popover on clear date

* style: make AppFlowyPopover taller

* fix: trim dateStr in _dateStrFromCellData

* fix: don't dismiss CellEditor after clearDate
This commit is contained in:
dejvizelo 2023-07-31 04:23:20 +02:00 committed by GitHub
parent ea37f46f88
commit f28c5d849c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 187 additions and 30 deletions

View File

@ -268,6 +268,18 @@ void main() {
content: DateFormat('dd/MM/y hh:mm a').format(now),
);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
await tester.findDateEditor(findsOneWidget);
// Clear the date and time
await tester.clearDate();
await tester.assertDateCellInGrid(
rowIndex: 0,
fieldType: fieldType,
content: '',
);
await tester.pumpAndSettle();
});

View File

@ -6,6 +6,7 @@ import 'package:appflowy/plugins/database_view/calendar/application/calendar_blo
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
@ -14,31 +15,42 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_menu_item.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/order_panel.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:calendar_view/calendar_view.dart';
@ -50,25 +62,12 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:path/path.dart' as p;
import 'package:table_calendar/table_calendar.dart';
import 'base.dart';
import 'common_operations.dart';
import 'expectation.dart';
import 'package:path/path.dart' as p;
import 'mock/mock_file_picker.dart';
extension AppFlowyDatabaseTest on WidgetTester {
@ -358,6 +357,16 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(findNewDateFormat);
}
Future<void> clearDate() async {
final findDateEditor = find.byType(DateCellEditor);
final findClearButton = find.byType(ClearDateButton);
final finder = find.descendant(
of: findDateEditor,
matching: findClearButton,
);
await tapButton(finder);
}
Future<void> tapSelectOptionCellInGrid({
required int rowIndex,
required FieldType fieldType,

View File

@ -35,6 +35,7 @@ class DateCellData with _$DateCellData {
DateTime? dateTime,
String? time,
required bool includeTime,
bool? clearFlag,
}) = _DateCellData;
}
@ -55,6 +56,9 @@ class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
if (data.time != null) {
payload.time = data.time!;
}
if (data.clearFlag != null) {
payload.clearFlag = data.clearFlag!;
}
payload.includeTime = data.includeTime;
return DatabaseEventUpdateDateCell(payload).send().then((result) {

View File

@ -1,18 +1,19 @@
import 'dart:async';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:easy_localization/easy_localization.dart'
show StringTranslateExtension;
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:easy_localization/easy_localization.dart'
show StringTranslateExtension;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:table_calendar/table_calendar.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'package:table_calendar/table_calendar.dart';
part 'date_cal_bloc.freezed.dart';
@ -64,6 +65,9 @@ class DateCellCalendarBloc
setFocusedDay: (focusedDay) {
emit(state.copyWith(focusedDay: focusedDay));
},
clearDate: () async {
await _clearDate(emit);
},
);
},
);
@ -121,6 +125,29 @@ class DateCellCalendarBloc
);
}
Future<void> _clearDate(Emitter<DateCellCalendarState> emit) async {
final DateCellData newDateData = DateCellData(
dateTime: null,
time: null,
includeTime: state.includeTime,
clearFlag: true,
);
cellController.saveCellData(
newDateData,
onFinish: (result) {
result.fold(
() {
if (!isClosed) {
add(const DateCellCalendarEvent.didReceiveTimeFormatError(null));
}
},
(err) => Log.error(err),
);
},
);
}
DateTime? _utcToLocalAddTime(DateTime? date) {
if (date == null) {
return null;
@ -237,6 +264,8 @@ class DateCellCalendarEvent with _$DateCellCalendarEvent {
_TimeFormat;
const factory DateCellCalendarEvent.setDateFormat(DateFormatPB dateFormat) =
_DateFormat;
const factory DateCellCalendarEvent.clearDate() = _ClearDate;
}
@freezed

View File

@ -1,9 +1,9 @@
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import '../../../../grid/presentation/layout/sizes.dart';
import '../../cell_builder.dart';
@ -83,7 +83,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
controller: _popover,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned,
constraints: BoxConstraints.loose(const Size(260, 500)),
constraints: BoxConstraints.loose(const Size(260, 520)),
margin: EdgeInsets.zero,
child: dateTextWidget,
popupBuilder: (BuildContext popoverContent) {

View File

@ -1,9 +1,11 @@
import 'dart:async';
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@ -85,5 +87,5 @@ String _dateStrFromCellData(DateCellDataPB? cellData) {
dateStr = cellData.date;
}
}
return dateStr;
return dateStr.trim();
}

View File

@ -3,20 +3,21 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:dartz/dartz.dart' show Either;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:table_calendar/table_calendar.dart';
import '../../../../grid/presentation/layout/sizes.dart';
import '../../../../grid/presentation/widgets/common/type_option_separator.dart';
import '../../../../grid/presentation/widgets/header/type_option/date.dart';
@ -123,7 +124,9 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
const TypeOptionSeparator(spacing: 12.0),
const _IncludeTimeButton(),
const TypeOptionSeparator(spacing: 12.0),
DateTypeOptionButton(popoverMutex: popoverMutex)
DateTypeOptionButton(popoverMutex: popoverMutex),
const TypeOptionSeparator(spacing: 12.0),
const ClearDateButton(),
];
return ListView.builder(
@ -458,3 +461,29 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
);
}
}
@visibleForTesting
class ClearDateButton extends StatelessWidget {
const ClearDateButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
text: FlowyText.medium(LocaleKeys.grid_field_clearDate.tr()),
onTap: () {
context
.read<DateCellCalendarBloc>()
.add(const DateCellCalendarEvent.clearDate());
PopoverContainer.of(context).close();
},
leftIcon: const FlowySvg(name: "grid/delete"),
margin: GridSize.typeOptionContentInsets,
),
),
);
}
}

View File

@ -378,6 +378,7 @@
"invalidTimeFormat": "Invalid format",
"timeFormatTwelveHour": "12 hour",
"timeFormatTwentyFourHour": "24 hour",
"clearDate": "Clear date",
"addSelectOption": "Add an option",
"optionTitle": "Options",
"addOption": "Add option",

View File

@ -35,6 +35,9 @@ pub struct DateChangesetPB {
#[pb(index = 4, one_of)]
pub include_time: Option<bool>,
#[pb(index = 5, one_of)]
pub clear_flag: Option<bool>,
}
// Date

View File

@ -640,6 +640,7 @@ pub(crate) async fn update_date_cell_handler(
date: data.date,
time: data.time,
include_time: data.include_time,
clear_flag: data.clear_flag,
};
let database_editor = manager.get_database_with_view_id(&cell_id.view_id).await?;
database_editor

View File

@ -212,6 +212,7 @@ pub fn insert_date_cell(timestamp: i64, include_time: Option<bool>, field: &Fiel
date: Some(timestamp.to_string()),
time: None,
include_time,
clear_flag: None,
})
.unwrap();
apply_cell_changeset(cell_data, None, field, None).unwrap()

View File

@ -27,6 +27,7 @@ mod tests {
date: Some("1647251762".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"Mar 14, 2022",
@ -40,6 +41,7 @@ mod tests {
date: Some("1647251762".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"2022/03/14",
@ -53,6 +55,7 @@ mod tests {
date: Some("1647251762".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"2022-03-14",
@ -66,6 +69,7 @@ mod tests {
date: Some("1647251762".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"03/14/2022",
@ -79,6 +83,7 @@ mod tests {
date: Some("1647251762".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"14/03/2022",
@ -104,6 +109,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: None,
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 00:00",
@ -115,6 +121,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("9:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 09:00",
@ -126,6 +133,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("23:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 23:00",
@ -139,6 +147,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: None,
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 12:00 AM",
@ -150,6 +159,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("9:00 AM".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 09:00 AM",
@ -161,6 +171,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("11:23 pm".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 11:23 PM",
@ -182,6 +193,7 @@ mod tests {
date: Some("abc".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
None,
"",
@ -202,6 +214,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("1:".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 01:00",
@ -222,6 +235,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 01:00",
@ -240,6 +254,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("00:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 00:00",
@ -260,6 +275,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("1:00 am".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 01:00 AM",
@ -283,6 +299,7 @@ mod tests {
date: Some("1653609600".to_owned()),
time: Some("20:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
None,
"May 27, 2022 08:00 PM",
@ -330,6 +347,7 @@ mod tests {
date: Some("1700006400".to_owned()),
time: Some("08:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
);
assert_date(
@ -339,6 +357,7 @@ mod tests {
date: Some("1701302400".to_owned()),
time: None,
include_time: None,
clear_flag: None,
},
Some(old_cell_data),
"Nov 30, 2023 08:00",
@ -356,6 +375,7 @@ mod tests {
date: Some("1700006400".to_owned()),
time: Some("08:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
);
assert_date(
@ -365,12 +385,41 @@ mod tests {
date: None,
time: Some("14:00".to_owned()),
include_time: None,
clear_flag: None,
},
Some(old_cell_data),
"Nov 15, 2023 14:00",
);
}
#[test]
fn clear_date() {
let type_option = DateTypeOption::test();
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
let old_cell_data = initialize_date_cell(
&type_option,
DateCellChangeset {
date: Some("1700006400".to_owned()),
time: Some("08:00".to_owned()),
include_time: Some(true),
clear_flag: None,
},
);
assert_date(
&type_option,
&field,
DateCellChangeset {
date: None,
time: None,
include_time: Some(true),
clear_flag: Some(true),
},
Some(old_cell_data),
"",
);
}
fn assert_date(
type_option: &DateTypeOption,
field: &Field,

View File

@ -232,6 +232,18 @@ impl CellDataChangeset for DateTypeOption {
None => (None, false),
};
if changeset.clear_flag == Some(true) {
let (timestamp, include_time) = (None, include_time);
let cell_data = DateCellData {
timestamp,
include_time,
};
let cell_wrapper: DateCellDataWrapper = (self.field_type.clone(), cell_data.clone()).into();
return Ok((Cell::from(cell_wrapper), cell_data));
}
// update include_time if necessary
let include_time = changeset.include_time.unwrap_or(include_time);

View File

@ -22,6 +22,7 @@ pub struct DateCellChangeset {
pub date: Option<String>,
pub time: Option<String>,
pub include_time: Option<bool>,
pub clear_flag: Option<bool>,
}
impl DateCellChangeset {

View File

@ -331,6 +331,7 @@ impl<'a> TestRowBuilder<'a> {
date: Some(data.to_string()),
time,
include_time,
clear_flag: None,
})
.unwrap();
let date_field = self.field_with_type(field_type);

View File

@ -83,6 +83,7 @@ pub fn make_date_cell_string(s: &str) -> String {
date: Some(s.to_string()),
time: None,
include_time: Some(false),
clear_flag: None,
})
.unwrap()
}

View File

@ -531,6 +531,7 @@ async fn update_date_cell_event_test() {
date: Some(timestamp_str.clone()),
time: None,
include_time: None,
clear_flag: None,
})
.await;
assert!(error.is_none());
@ -895,6 +896,7 @@ async fn create_calendar_event_test() {
date: Some(timestamp_str.clone()),
time: None,
include_time: None,
clear_flag: None,
})
.await;
assert!(error.is_none());