From 4b88aa868364e633ff367a1bf0e9e9a758aa8ad9 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 15:06:15 +0800 Subject: [PATCH 1/3] fix: edit date time issue --- .../app_flowy/assets/translations/en.json | 4 + .../cell/cell_service/context_builder.dart | 2 +- .../cell/cell_service/data_persistence.dart | 8 +- .../application/grid/cell/date_cal_bloc.dart | 69 +++--- .../application/grid/cell/date_cell_bloc.dart | 26 +-- .../src/widgets/cell/date_cell/date_cell.dart | 2 +- .../widgets/cell/date_cell/date_editor.dart | 12 +- .../type_options/checkbox_type_option.rs | 2 +- .../field/type_options/date_type_option.rs | 205 ++++++++++-------- .../number_type_option/number_type_option.rs | 2 +- .../type_options/selection_type_option.rs | 4 +- .../field/type_options/text_type_option.rs | 4 +- .../field/type_options/url_type_option.rs | 2 +- .../src/services/row/cell_data_operation.rs | 10 +- 14 files changed, 202 insertions(+), 150 deletions(-) diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index e465e79607..dfc80b402b 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -198,6 +198,10 @@ "colorPannelTitle": "Colors", "pannelTitle": "Select an option or create one", "searchOption": "Search for an option" + }, + "date": { + "timeHintTextInTwelveHour": "12:00 AM", + "timeHintTextInTwentyFourHour": "12:00" } } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 6c0fb560e8..4a27b10e1b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -2,7 +2,7 @@ part of 'cell_service.dart'; typedef GridCellContext = _GridCellContext; typedef GridSelectOptionCellContext = _GridCellContext; -typedef GridDateCellContext = _GridCellContext; +typedef GridDateCellContext = _GridCellContext; typedef GridURLCellContext = _GridCellContext; class GridCellContextBuilder { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart index e09a528e44..2ad217e062 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart @@ -31,18 +31,18 @@ class CellDataPersistence implements _GridCellDataPersistence { } @freezed -class DateCalData with _$DateCalData { - const factory DateCalData({required DateTime date, String? time}) = _DateCellPersistenceData; +class CalendarData with _$CalendarData { + const factory CalendarData({required DateTime date, String? time}) = _CalendarData; } -class DateCellDataPersistence implements _GridCellDataPersistence { +class DateCellDataPersistence implements _GridCellDataPersistence { final GridCell gridCell; DateCellDataPersistence({ required this.gridCell, }); @override - Future> save(DateCalData data) { + Future> save(CalendarData data) { var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell); final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString(); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart index 15f18707f8..c72a10b481 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart @@ -38,9 +38,9 @@ class DateCalBloc extends Bloc { emit(state.copyWith(focusedDay: focusedDay)); }, didReceiveCellUpdate: (DateCellData? cellData) { - final dateData = dateDataFromCellData(cellData); - final time = dateData.foldRight("", (dateData, previous) => dateData.time); - emit(state.copyWith(dateData: dateData, time: time)); + final calData = calDataFromCellData(cellData); + final time = calData.foldRight("", (dateData, previous) => dateData.time); + emit(state.copyWith(calData: calData, time: time)); }, setIncludeTime: (includeTime) async { await _updateTypeOption(emit, includeTime: includeTime); @@ -52,7 +52,12 @@ class DateCalBloc extends Bloc { await _updateTypeOption(emit, timeFormat: timeFormat); }, setTime: (time) async { - await _updateDateData(emit, time: time); + if (state.calData.isSome()) { + await _updateDateData(emit, time: time); + } + }, + didUpdateCalData: (Option data, Option timeFormatError) { + emit(state.copyWith(calData: data, timeFormatError: timeFormatError)); }, ); }, @@ -60,8 +65,8 @@ class DateCalBloc extends Bloc { } Future _updateDateData(Emitter emit, {DateTime? date, String? time}) { - final DateCalData newDateData = state.dateData.fold( - () => DateCalData(date: date ?? DateTime.now(), time: time), + final CalendarData newDateData = state.calData.fold( + () => CalendarData(date: date ?? DateTime.now(), time: time), (dateData) { var newDateData = dateData; if (date != null && !isSameDay(newDateData.date, date)) { @@ -78,24 +83,22 @@ class DateCalBloc extends Bloc { return _saveDateData(emit, newDateData); } - Future _saveDateData(Emitter emit, DateCalData newDateData) async { - if (state.dateData == Some(newDateData)) { + Future _saveDateData(Emitter emit, CalendarData newCalData) async { + if (state.calData == Some(newCalData)) { return; } - cellContext.saveCellData(newDateData, resultCallback: (result) { + updateCalData(Option calData, Option timeFormatError) { + if (!isClosed) add(DateCalEvent.didUpdateCalData(calData, timeFormatError)); + } + + cellContext.saveCellData(newCalData, resultCallback: (result) { result.fold( - () => emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: none(), - )), + () => updateCalData(Some(newCalData), none()), (err) { switch (ErrorCode.valueOf(err.code)!) { case ErrorCode.InvalidDateTimeFormat: - emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: Some(timeFormatPrompt(err)), - )); + updateCalData(none(), Some(timeFormatPrompt(err))); break; default: Log.error(err); @@ -168,7 +171,7 @@ class DateCalBloc extends Bloc { ); result.fold( - (l) => emit(state.copyWith(dateTypeOption: newDateTypeOption)), + (l) => emit(state.copyWith(dateTypeOption: newDateTypeOption, timeHintText: _timeHintText(newDateTypeOption))), (err) => Log.error(err), ); } @@ -185,6 +188,8 @@ class DateCalEvent with _$DateCalEvent { const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime; const factory DateCalEvent.setTime(String time) = _Time; const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; + const factory DateCalEvent.didUpdateCalData(Option data, Option timeFormatError) = + _DidUpdateCalData; } @freezed @@ -194,36 +199,48 @@ class DateCalState with _$DateCalState { required CalendarFormat format, required DateTime focusedDay, required Option timeFormatError, - required Option dateData, + required Option calData, required String? time, + required String timeHintText, }) = _DateCalState; factory DateCalState.initial( DateTypeOption dateTypeOption, DateCellData? cellData, ) { - Option dateData = dateDataFromCellData(cellData); - final time = dateData.foldRight("", (dateData, previous) => dateData.time); + Option calData = calDataFromCellData(cellData); + final time = calData.foldRight("", (dateData, previous) => dateData.time); return DateCalState( dateTypeOption: dateTypeOption, format: CalendarFormat.month, focusedDay: DateTime.now(), time: time, - dateData: dateData, + calData: calData, timeFormatError: none(), + timeHintText: _timeHintText(dateTypeOption), ); } } -Option dateDataFromCellData(DateCellData? cellData) { +String _timeHintText(DateTypeOption typeOption) { + switch (typeOption.timeFormat) { + case TimeFormat.TwelveHour: + return LocaleKeys.grid_date_timeHintTextInTwelveHour.tr(); + case TimeFormat.TwentyFourHour: + return LocaleKeys.grid_date_timeHintTextInTwentyFourHour.tr(); + } + return ""; +} + +Option calDataFromCellData(DateCellData? cellData) { String? time = timeFromCellData(cellData); - Option dateData = none(); + Option calData = none(); if (cellData != null) { final timestamp = cellData.timestamp * 1000; final date = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt()); - dateData = Some(DateCalData(date: date, time: time)); + calData = Some(CalendarData(date: date, time: time)); } - return dateData; + return calData; } $fixnum.Int64 timestampFromDateTime(DateTime dateTime) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart index b06a3d60b3..b9f4c74070 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'cell_service/cell_service.dart'; -import 'package:dartz/dartz.dart'; part 'date_cell_bloc.freezed.dart'; class DateCellBloc extends Bloc { @@ -17,11 +16,7 @@ class DateCellBloc extends Bloc { event.when( initial: () => _startListening(), didReceiveCellUpdate: (DateCellData? cellData) { - if (cellData != null) { - emit(state.copyWith(data: Some(cellData))); - } else { - emit(state.copyWith(data: none())); - } + emit(state.copyWith(data: cellData, dateStr: _dateStrFromCellData(cellData))); }, didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)), ); @@ -60,21 +55,26 @@ class DateCellEvent with _$DateCellEvent { @freezed class DateCellState with _$DateCellState { const factory DateCellState({ - required Option data, + required DateCellData? data, + required String dateStr, required Field field, }) = _DateCellState; factory DateCellState.initial(GridDateCellContext context) { final cellData = context.getCellData(); - Option data = none(); - - if (cellData != null) { - data = Some(cellData); - } return DateCellState( field: context.field, - data: data, + data: cellData, + dateStr: _dateStrFromCellData(cellData), ); } } + +String _dateStrFromCellData(DateCellData? cellData) { + String dateStr = ""; + if (cellData != null) { + dateStr = cellData.date + " " + cellData.time; + } + return dateStr; +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart index c644ac61a5..9855b98bf5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart @@ -64,7 +64,7 @@ class _DateCellState extends State { cursor: SystemMouseCursors.click, child: Align( alignment: alignment, - child: FlowyText.medium(state.data.foldRight("", (data, _) => data.date), fontSize: 12), + child: FlowyText.medium(state.dateStr, fontSize: 12), ), ), ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart index 016eb00e85..93d304cd1a 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart @@ -160,18 +160,21 @@ class _CellCalendarWidget extends StatelessWidget { ), ), selectedDayPredicate: (day) { - return state.dateData.fold( + return state.calData.fold( () => false, (dateData) => isSameDay(dateData.date, day), ); }, onDaySelected: (selectedDay, focusedDay) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.selectDay(selectedDay)); }, onFormatChanged: (format) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.setCalFormat(format)); }, onPageChanged: (focusedDay) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.setFocusedDay(focusedDay)); }, ); @@ -234,6 +237,7 @@ class _TimeTextFieldState extends State<_TimeTextField> { if (widget.bloc.state.dateTypeOption.includeTime) { _focusNode.addListener(() { if (mounted) { + _CalDateTimeSetting.hide(context); widget.bloc.add(DateCalEvent.setTime(_controller.text)); } }); @@ -257,6 +261,7 @@ class _TimeTextFieldState extends State<_TimeTextField> { child: RoundedInputField( height: 40, focusNode: _focusNode, + hintText: state.timeHintText, controller: _controller, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), normalBorderColor: theme.shader4, @@ -326,6 +331,7 @@ class _CalDateTimeSetting extends StatefulWidget { } void show(BuildContext context) { + hide(context); FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( child: this, @@ -337,6 +343,10 @@ class _CalDateTimeSetting extends StatefulWidget { anchorOffset: const Offset(20, 0), ); } + + static void hide(BuildContext context) { + FlowyOverlay.of(context).remove(identifier()); + } } class _CalDateTimeSettingState extends State<_CalDateTimeSetting> { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs index c8d14b6b03..995a0e5e33 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs @@ -42,7 +42,7 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); const YES: &str = "Yes"; const NO: &str = "No"; -impl CellDataOperation for CheckboxTypeOption { +impl CellDataOperation for CheckboxTypeOption { fn decode_cell_data( &self, encoded_data: T, diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index c96166272a..2323a6eb20 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -4,7 +4,7 @@ use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; -use chrono::NaiveDateTime; +use chrono::{NaiveDateTime, Timelike}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ @@ -29,35 +29,36 @@ pub struct DateTypeOption { impl_type_option!(DateTypeOption, FieldType::DateTime); impl DateTypeOption { - fn today_desc_from_timestamp(&self, timestamp: i64, time: &Option) -> String { - let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); - self.today_desc_from_native(native, time) - } - #[allow(dead_code)] - fn today_desc_from_str(&self, s: String, time: &Option) -> String { - match NaiveDateTime::parse_from_str(&s, &self.date_fmt(time)) { - Ok(native) => self.today_desc_from_native(native, time), - Err(_) => "".to_owned(), + pub fn new() -> Self { + Self::default() + } + + fn today_desc_from_timestamp(&self, timestamp: i64) -> DateCellData { + let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); + self.date_from_native(native) + } + + fn date_from_native(&self, native: chrono::NaiveDateTime) -> DateCellData { + if native.timestamp() == 0 { + return DateCellData::default(); } - } - fn today_desc_from_native(&self, native: chrono::NaiveDateTime, time: &Option) -> String { + let time = native.time(); + let has_time = time.hour() != 0 || time.second() != 0; + let utc = self.utc_date_time_from_native(native); - // let china_timezone = FixedOffset::east(8 * 3600); - // let a = utc.with_timezone(&china_timezone); - let fmt = self.date_fmt(time); - let output = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))); - output - } + let fmt = self.date_format.format_str(); + let date = format!("{}", utc.format_with_items(StrftimeItems::new(fmt))); - fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { - let native = NaiveDateTime::from_timestamp(timestamp, 0); - self.utc_date_time_from_native(native) - } + let mut time = "".to_string(); + if has_time { + let fmt = format!("{} {}", self.date_format.format_str(), self.time_format.format_str()); + time = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))).replace(&date, ""); + } - fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { - chrono::DateTime::::from_utc(naive, chrono::Utc) + let timestamp = native.timestamp(); + DateCellData { date, time, timestamp } } fn date_fmt(&self, time: &Option) -> String { @@ -77,14 +78,6 @@ impl DateTypeOption { } } - fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String { - if serde_cell_data.timestamp == 0 { - return "".to_owned(); - } - - self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time) - } - fn timestamp_from_utc_with_time( &self, utc: &chrono::DateTime, @@ -113,9 +106,18 @@ impl DateTypeOption { Ok(utc.timestamp()) } + + fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { + let native = NaiveDateTime::from_timestamp(timestamp, 0); + self.utc_date_time_from_native(native) + } + + fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { + chrono::DateTime::::from_utc(naive, chrono::Utc) + } } -impl CellDataOperation, DateCellDataSerde> for DateTypeOption { +impl CellDataOperation> for DateTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -134,14 +136,11 @@ impl CellDataOperation, DateCellDataSerde> fo } let encoded_data = encoded_data.into().try_into_inner()?; - let date = self.date_desc_from_timestamp(&encoded_data); - let time = encoded_data.time.unwrap_or_else(|| "".to_owned()); - let timestamp = encoded_data.timestamp; - - DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp }) + let date = self.today_desc_from_timestamp(encoded_data.timestamp); + DecodedCellData::try_from_bytes(date) } - fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result where C: Into, { @@ -153,13 +152,13 @@ impl CellDataOperation, DateCellDataSerde> fo let time = Some(time.trim().to_uppercase()); let utc = self.utc_date_time_from_timestamp(date_timestamp); let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; - DateCellDataSerde::new(timestamp, time, &self.time_format) + DateCellDataSerde::new(timestamp, time) } - _ => DateCellDataSerde::from_timestamp(date_timestamp, Some(default_time_str(&self.time_format))), + _ => DateCellDataSerde::new(date_timestamp, None), }, }; - Ok(cell_data) + Ok(cell_data.to_string()) } } @@ -284,21 +283,16 @@ pub struct DateCellData { } #[derive(Default, Serialize, Deserialize)] -pub struct DateCellDataSerde { +pub(crate) struct DateCellDataSerde { pub timestamp: i64, + + // #[deprecated(since = "0.0.4", note = "No need to same the time that user input")] pub time: Option, } impl DateCellDataSerde { - fn new(timestamp: i64, time: Option, time_format: &TimeFormat) -> Self { - Self { - timestamp, - time: Some(time.unwrap_or_else(|| default_time_str(time_format))), - } - } - - pub(crate) fn from_timestamp(timestamp: i64, time: Option) -> Self { - Self { timestamp, time } + pub(crate) fn new(timestamp: i64, _time: Option) -> Self { + Self { timestamp, time: None } } } @@ -316,13 +310,6 @@ impl ToString for DateCellDataSerde { } } -fn default_time_str(time_format: &TimeFormat) -> String { - match time_format { - TimeFormat::TwelveHour => "12:00 AM".to_string(), - TimeFormat::TwentyFourHour => "00:00".to_string(), - } -} - #[derive(Clone, Debug, Default, ProtoBuf)] pub struct DateChangesetPayload { #[pb(index = 1)] @@ -403,11 +390,11 @@ mod tests { DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, }; use crate::services::row::{CellDataOperation, EncodedCellData}; - use flowy_grid_data_model::entities::{FieldMeta, FieldType}; + use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; #[test] - fn date_description_invalid_input_test() { + fn date_type_option_invalid_input_test() { let type_option = DateTypeOption::default(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); @@ -424,7 +411,7 @@ mod tests { } #[test] - fn date_description_date_format_test() { + fn date_type_option_date_format_test() { let mut type_option = DateTypeOption::default(); let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); for date_format in DateFormat::iter() { @@ -447,7 +434,7 @@ mod tests { } #[test] - fn date_description_time_format_test() { + fn date_type_option_time_format_test() { let mut type_option = DateTypeOption::default(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); @@ -465,7 +452,7 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 00:00", + "May 27,2022", ); assert_changeset_result( &type_option, @@ -487,9 +474,9 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 12:00 AM", + "May 27,2022", ); - + // assert_changeset_result( &type_option, DateCellContentChangeset { @@ -517,8 +504,8 @@ mod tests { } #[test] - fn date_description_apply_changeset_test() { - let mut type_option = DateTypeOption::default(); + fn date_type_option_apply_changeset_test() { + let mut type_option = DateTypeOption::new(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); let date_timestamp = "1653609600".to_owned(); @@ -543,7 +530,7 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 00:00", + "May 27,2022", ); assert_changeset_result( @@ -572,30 +559,53 @@ mod tests { #[test] #[should_panic] - fn date_description_apply_changeset_error_test() { - let mut type_option = DateTypeOption::default(); + fn date_type_option_apply_changeset_error_test() { + let mut type_option = DateTypeOption::new(); type_option.include_time = true; - let _field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let field_meta = FieldBuilder::from_field_type(&type_option.field_type()).build(); let date_timestamp = "1653609600".to_owned(); - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:a0".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); - let changeset = DateCellContentChangeset { - date: Some(date_timestamp), - time: Some("1:".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); } #[test] #[should_panic] - fn date_description_invalid_data_test() { - let type_option = DateTypeOption::default(); - type_option.apply_changeset("he", None).unwrap(); + fn date_type_option_twelve_hours_to_twenty_four_hours() { + let mut type_option = DateTypeOption::new(); + type_option.include_time = true; + let field_meta = FieldBuilder::from_field_type(&type_option.field_type()).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00 am".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); } fn assert_changeset_result( @@ -605,7 +615,7 @@ mod tests { field_meta: &FieldMeta, expected: &str, ) { - let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap())); + let encoded_data = type_option.apply_changeset(changeset, None).unwrap(); assert_eq!( expected.to_owned(), decode_cell_data(encoded_data, type_option, field_meta) @@ -613,11 +623,19 @@ mod tests { } fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) { - let serde_json = DateCellDataSerde { timestamp, time: None }.to_string(); + let encoded_data = type_option + .apply_changeset( + DateCellContentChangeset { + date: Some(timestamp.to_string()), + time: None, + }, + None, + ) + .unwrap(); assert_eq!( expected.to_owned(), - decode_cell_data(serde_json, type_option, field_meta) + decode_cell_data(encoded_data, type_option, field_meta) ); } @@ -626,11 +644,16 @@ mod tests { type_option: &DateTypeOption, field_meta: &FieldMeta, ) -> String { - type_option + let decoded_data = type_option .decode_cell_data(encoded_data, &FieldType::DateTime, field_meta) .unwrap() .parse::() - .unwrap() - .date + .unwrap(); + + if type_option.include_time { + format!("{}{}", decoded_data.date, decoded_data.time) + } else { + format!("{}", decoded_data.date) + } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs index f1000cbcfa..bb3130c597 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -71,7 +71,7 @@ pub struct NumberTypeOption { } impl_type_option!(NumberTypeOption, FieldType::Number); -impl CellDataOperation for NumberTypeOption { +impl CellDataOperation for NumberTypeOption { fn decode_cell_data( &self, encoded_data: T, diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs index 81a7ff5c04..a7ad30cc31 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs @@ -95,7 +95,7 @@ impl SelectOptionOperation for SingleSelectTypeOption { } } -impl CellDataOperation for SingleSelectTypeOption { +impl CellDataOperation for SingleSelectTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -193,7 +193,7 @@ impl SelectOptionOperation for MultiSelectTypeOption { } } -impl CellDataOperation for MultiSelectTypeOption { +impl CellDataOperation for MultiSelectTypeOption { fn decode_cell_data( &self, encoded_data: T, diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index 3509972670..029d476533 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -31,7 +31,7 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); -impl CellDataOperation for RichTextTypeOption { +impl CellDataOperation for RichTextTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -80,7 +80,7 @@ mod tests { // date let field_type = FieldType::DateTime; let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); - let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); + let json = serde_json::to_string(&DateCellDataSerde::new(1647251762, None)).unwrap(); assert_eq!( type_option .decode_cell_data(json, &field_type, &date_time_field_meta) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 0d5f37baaf..ecb2a8e16f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -34,7 +34,7 @@ pub struct URLTypeOption { } impl_type_option!(URLTypeOption, FieldType::URL); -impl CellDataOperation, String> for URLTypeOption { +impl CellDataOperation> for URLTypeOption { fn decode_cell_data( &self, encoded_data: T, diff --git a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs index d93f84244a..7423bf0fcd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Formatter; use std::str::FromStr; -pub trait CellDataOperation { +pub trait CellDataOperation { fn decode_cell_data( &self, encoded_data: T, @@ -14,14 +14,14 @@ pub trait CellDataOperation { field_meta: &FieldMeta, ) -> FlowyResult where - T: Into; + T: Into; // fn apply_changeset>( &self, changeset: C, cell_meta: Option, - ) -> FlowyResult; + ) -> FlowyResult; } #[derive(Debug)] @@ -128,9 +128,7 @@ pub fn apply_cell_data_changeset>( let s = match field_meta.field_type { FieldType::RichText => RichTextTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Number => NumberTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), - FieldType::DateTime => DateTypeOption::from(field_meta) - .apply_changeset(changeset, cell_meta) - .map(|data| data.to_string()), + FieldType::DateTime => DateTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), From cad5e5d718430583bd63502275fa5f2cd98928d3 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 18:15:10 +0800 Subject: [PATCH 2/3] refactor: Date's cell data serde --- .../cell/cell_service/context_builder.dart | 1 + .../field/type_options/date_type_option.rs | 53 ++++--------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 4a27b10e1b..b554f03556 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -31,6 +31,7 @@ class GridCellContextBuilder { final cellDataLoader = GridCellDataLoader( gridCell: _gridCell, parser: DateCellDataParser(), + config: const GridCellDataConfig(reloadOnFieldChanged: true), ); return GridDateCellContext( diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 2323a6eb20..891eb2f336 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -1,17 +1,16 @@ use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; use chrono::{NaiveDateTime, Timelike}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; +use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellChangeset, CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use strum_macros::EnumIter; // Date @@ -117,7 +116,7 @@ impl DateTypeOption { } } -impl CellDataOperation> for DateTypeOption { +impl CellDataOperation for DateTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -125,7 +124,7 @@ impl CellDataOperation> for DateTypeOption { _field_meta: &FieldMeta, ) -> FlowyResult where - T: Into>, + T: Into, { // Return default data if the type_option_cell_data is not FieldType::DateTime. // It happens when switching from one field to another. @@ -135,8 +134,8 @@ impl CellDataOperation> for DateTypeOption { return Ok(DecodedCellData::default()); } - let encoded_data = encoded_data.into().try_into_inner()?; - let date = self.today_desc_from_timestamp(encoded_data.timestamp); + let timestamp = encoded_data.into().parse::().unwrap_or(0); + let date = self.today_desc_from_timestamp(timestamp); DecodedCellData::try_from_bytes(date) } @@ -146,15 +145,15 @@ impl CellDataOperation> for DateTypeOption { { let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?; let cell_data = match content_changeset.date_timestamp() { - None => DateCellDataSerde::default(), + None => 0, Some(date_timestamp) => match (self.include_time, content_changeset.time) { (true, Some(time)) => { let time = Some(time.trim().to_uppercase()); let utc = self.utc_date_time_from_timestamp(date_timestamp); let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; - DateCellDataSerde::new(timestamp, time) + timestamp } - _ => DateCellDataSerde::new(date_timestamp, None), + _ => date_timestamp, }, }; @@ -282,34 +281,6 @@ pub struct DateCellData { pub timestamp: i64, } -#[derive(Default, Serialize, Deserialize)] -pub(crate) struct DateCellDataSerde { - pub timestamp: i64, - - // #[deprecated(since = "0.0.4", note = "No need to same the time that user input")] - pub time: Option, -} - -impl DateCellDataSerde { - pub(crate) fn new(timestamp: i64, _time: Option) -> Self { - Self { timestamp, time: None } - } -} - -impl FromStr for DateCellDataSerde { - type Err = FlowyError; - - fn from_str(s: &str) -> Result { - serde_json::from_str::(s).map_err(internal_error) - } -} - -impl ToString for DateCellDataSerde { - fn to_string(&self) -> String { - serde_json::to_string(&self).unwrap_or_else(|_| "".to_string()) - } -} - #[derive(Clone, Debug, Default, ProtoBuf)] pub struct DateChangesetPayload { #[pb(index = 1)] @@ -386,9 +357,7 @@ impl std::convert::From for CellContentChangeset { #[cfg(test)] mod tests { use crate::services::field::FieldBuilder; - use crate::services::field::{ - DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, - }; + use crate::services::field::{DateCellContentChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; use crate::services::row::{CellDataOperation, EncodedCellData}; use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; @@ -639,7 +608,7 @@ mod tests { ); } - fn decode_cell_data>>( + fn decode_cell_data>( encoded_data: T, type_option: &DateTypeOption, field_meta: &FieldMeta, From 481fd9df514334ac55fa6ec553651dc3cee7d2c4 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 20:24:25 +0800 Subject: [PATCH 3/3] fix: rust unit test --- .../application/grid/cell/cell_service/context_builder.dart | 1 + .../src/services/field/type_options/date_type_option.rs | 2 +- .../src/services/field/type_options/text_type_option.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index b554f03556..3041c563d9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -219,6 +219,7 @@ class _GridCellContext extends Equatable { } void dispose() { + _cellListener.stop(); _loadDataOperation?.cancel(); _saveDataOperation?.cancel(); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 891eb2f336..f91fc8bb48 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -358,7 +358,7 @@ impl std::convert::From for CellContentChangeset { mod tests { use crate::services::field::FieldBuilder; use crate::services::field::{DateCellContentChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; - use crate::services::row::{CellDataOperation, EncodedCellData}; + use crate::services::row::CellDataOperation; use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index 029d476533..b59681a2ea 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -80,10 +80,10 @@ mod tests { // date let field_type = FieldType::DateTime; let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); - let json = serde_json::to_string(&DateCellDataSerde::new(1647251762, None)).unwrap(); + assert_eq!( type_option - .decode_cell_data(json, &field_type, &date_time_field_meta) + .decode_cell_data(1647251762.to_string(), &field_type, &date_time_field_meta) .unwrap() .parse::() .unwrap()