fix: edit date time issue

This commit is contained in:
appflowy 2022-06-02 15:06:15 +08:00
parent a384404814
commit 4b88aa8683
14 changed files with 202 additions and 150 deletions

View File

@ -198,6 +198,10 @@
"colorPannelTitle": "Colors", "colorPannelTitle": "Colors",
"pannelTitle": "Select an option or create one", "pannelTitle": "Select an option or create one",
"searchOption": "Search for an option" "searchOption": "Search for an option"
},
"date": {
"timeHintTextInTwelveHour": "12:00 AM",
"timeHintTextInTwentyFourHour": "12:00"
} }
} }
} }

View File

@ -2,7 +2,7 @@ part of 'cell_service.dart';
typedef GridCellContext = _GridCellContext<String, String>; typedef GridCellContext = _GridCellContext<String, String>;
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>; typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>; typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
typedef GridURLCellContext = _GridCellContext<URLCellData, String>; typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
class GridCellContextBuilder { class GridCellContextBuilder {

View File

@ -31,18 +31,18 @@ class CellDataPersistence implements _GridCellDataPersistence<String> {
} }
@freezed @freezed
class DateCalData with _$DateCalData { class CalendarData with _$CalendarData {
const factory DateCalData({required DateTime date, String? time}) = _DateCellPersistenceData; const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
} }
class DateCellDataPersistence implements _GridCellDataPersistence<DateCalData> { class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> {
final GridCell gridCell; final GridCell gridCell;
DateCellDataPersistence({ DateCellDataPersistence({
required this.gridCell, required this.gridCell,
}); });
@override @override
Future<Option<FlowyError>> save(DateCalData data) { Future<Option<FlowyError>> save(CalendarData data) {
var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell); var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell);
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString(); final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();

View File

@ -38,9 +38,9 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
emit(state.copyWith(focusedDay: focusedDay)); emit(state.copyWith(focusedDay: focusedDay));
}, },
didReceiveCellUpdate: (DateCellData? cellData) { didReceiveCellUpdate: (DateCellData? cellData) {
final dateData = dateDataFromCellData(cellData); final calData = calDataFromCellData(cellData);
final time = dateData.foldRight("", (dateData, previous) => dateData.time); final time = calData.foldRight("", (dateData, previous) => dateData.time);
emit(state.copyWith(dateData: dateData, time: time)); emit(state.copyWith(calData: calData, time: time));
}, },
setIncludeTime: (includeTime) async { setIncludeTime: (includeTime) async {
await _updateTypeOption(emit, includeTime: includeTime); await _updateTypeOption(emit, includeTime: includeTime);
@ -52,7 +52,12 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
await _updateTypeOption(emit, timeFormat: timeFormat); await _updateTypeOption(emit, timeFormat: timeFormat);
}, },
setTime: (time) async { setTime: (time) async {
if (state.calData.isSome()) {
await _updateDateData(emit, time: time); await _updateDateData(emit, time: time);
}
},
didUpdateCalData: (Option<CalendarData> data, Option<String> timeFormatError) {
emit(state.copyWith(calData: data, timeFormatError: timeFormatError));
}, },
); );
}, },
@ -60,8 +65,8 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
} }
Future<void> _updateDateData(Emitter<DateCalState> emit, {DateTime? date, String? time}) { Future<void> _updateDateData(Emitter<DateCalState> emit, {DateTime? date, String? time}) {
final DateCalData newDateData = state.dateData.fold( final CalendarData newDateData = state.calData.fold(
() => DateCalData(date: date ?? DateTime.now(), time: time), () => CalendarData(date: date ?? DateTime.now(), time: time),
(dateData) { (dateData) {
var newDateData = dateData; var newDateData = dateData;
if (date != null && !isSameDay(newDateData.date, date)) { if (date != null && !isSameDay(newDateData.date, date)) {
@ -78,24 +83,22 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
return _saveDateData(emit, newDateData); return _saveDateData(emit, newDateData);
} }
Future<void> _saveDateData(Emitter<DateCalState> emit, DateCalData newDateData) async { Future<void> _saveDateData(Emitter<DateCalState> emit, CalendarData newCalData) async {
if (state.dateData == Some(newDateData)) { if (state.calData == Some(newCalData)) {
return; return;
} }
cellContext.saveCellData(newDateData, resultCallback: (result) { updateCalData(Option<CalendarData> calData, Option<String> timeFormatError) {
if (!isClosed) add(DateCalEvent.didUpdateCalData(calData, timeFormatError));
}
cellContext.saveCellData(newCalData, resultCallback: (result) {
result.fold( result.fold(
() => emit(state.copyWith( () => updateCalData(Some(newCalData), none()),
dateData: Some(newDateData),
timeFormatError: none(),
)),
(err) { (err) {
switch (ErrorCode.valueOf(err.code)!) { switch (ErrorCode.valueOf(err.code)!) {
case ErrorCode.InvalidDateTimeFormat: case ErrorCode.InvalidDateTimeFormat:
emit(state.copyWith( updateCalData(none(), Some(timeFormatPrompt(err)));
dateData: Some(newDateData),
timeFormatError: Some(timeFormatPrompt(err)),
));
break; break;
default: default:
Log.error(err); Log.error(err);
@ -168,7 +171,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
); );
result.fold( result.fold(
(l) => emit(state.copyWith(dateTypeOption: newDateTypeOption)), (l) => emit(state.copyWith(dateTypeOption: newDateTypeOption, timeHintText: _timeHintText(newDateTypeOption))),
(err) => Log.error(err), (err) => Log.error(err),
); );
} }
@ -185,6 +188,8 @@ class DateCalEvent with _$DateCalEvent {
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime; const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
const factory DateCalEvent.setTime(String time) = _Time; const factory DateCalEvent.setTime(String time) = _Time;
const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
const factory DateCalEvent.didUpdateCalData(Option<CalendarData> data, Option<String> timeFormatError) =
_DidUpdateCalData;
} }
@freezed @freezed
@ -194,36 +199,48 @@ class DateCalState with _$DateCalState {
required CalendarFormat format, required CalendarFormat format,
required DateTime focusedDay, required DateTime focusedDay,
required Option<String> timeFormatError, required Option<String> timeFormatError,
required Option<DateCalData> dateData, required Option<CalendarData> calData,
required String? time, required String? time,
required String timeHintText,
}) = _DateCalState; }) = _DateCalState;
factory DateCalState.initial( factory DateCalState.initial(
DateTypeOption dateTypeOption, DateTypeOption dateTypeOption,
DateCellData? cellData, DateCellData? cellData,
) { ) {
Option<DateCalData> dateData = dateDataFromCellData(cellData); Option<CalendarData> calData = calDataFromCellData(cellData);
final time = dateData.foldRight("", (dateData, previous) => dateData.time); final time = calData.foldRight("", (dateData, previous) => dateData.time);
return DateCalState( return DateCalState(
dateTypeOption: dateTypeOption, dateTypeOption: dateTypeOption,
format: CalendarFormat.month, format: CalendarFormat.month,
focusedDay: DateTime.now(), focusedDay: DateTime.now(),
time: time, time: time,
dateData: dateData, calData: calData,
timeFormatError: none(), timeFormatError: none(),
timeHintText: _timeHintText(dateTypeOption),
); );
} }
} }
Option<DateCalData> 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<CalendarData> calDataFromCellData(DateCellData? cellData) {
String? time = timeFromCellData(cellData); String? time = timeFromCellData(cellData);
Option<DateCalData> dateData = none(); Option<CalendarData> calData = none();
if (cellData != null) { if (cellData != null) {
final timestamp = cellData.timestamp * 1000; final timestamp = cellData.timestamp * 1000;
final date = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt()); 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) { $fixnum.Int64 timestampFromDateTime(DateTime dateTime) {

View File

@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
import 'cell_service/cell_service.dart'; import 'cell_service/cell_service.dart';
import 'package:dartz/dartz.dart';
part 'date_cell_bloc.freezed.dart'; part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> { class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@ -17,11 +16,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
event.when( event.when(
initial: () => _startListening(), initial: () => _startListening(),
didReceiveCellUpdate: (DateCellData? cellData) { didReceiveCellUpdate: (DateCellData? cellData) {
if (cellData != null) { emit(state.copyWith(data: cellData, dateStr: _dateStrFromCellData(cellData)));
emit(state.copyWith(data: Some(cellData)));
} else {
emit(state.copyWith(data: none()));
}
}, },
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)), didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
); );
@ -60,21 +55,26 @@ class DateCellEvent with _$DateCellEvent {
@freezed @freezed
class DateCellState with _$DateCellState { class DateCellState with _$DateCellState {
const factory DateCellState({ const factory DateCellState({
required Option<DateCellData> data, required DateCellData? data,
required String dateStr,
required Field field, required Field field,
}) = _DateCellState; }) = _DateCellState;
factory DateCellState.initial(GridDateCellContext context) { factory DateCellState.initial(GridDateCellContext context) {
final cellData = context.getCellData(); final cellData = context.getCellData();
Option<DateCellData> data = none();
if (cellData != null) {
data = Some(cellData);
}
return DateCellState( return DateCellState(
field: context.field, 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;
}

View File

@ -64,7 +64,7 @@ class _DateCellState extends State<DateCell> {
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
child: Align( child: Align(
alignment: alignment, alignment: alignment,
child: FlowyText.medium(state.data.foldRight("", (data, _) => data.date), fontSize: 12), child: FlowyText.medium(state.dateStr, fontSize: 12),
), ),
), ),
), ),

View File

@ -160,18 +160,21 @@ class _CellCalendarWidget extends StatelessWidget {
), ),
), ),
selectedDayPredicate: (day) { selectedDayPredicate: (day) {
return state.dateData.fold( return state.calData.fold(
() => false, () => false,
(dateData) => isSameDay(dateData.date, day), (dateData) => isSameDay(dateData.date, day),
); );
}, },
onDaySelected: (selectedDay, focusedDay) { onDaySelected: (selectedDay, focusedDay) {
_CalDateTimeSetting.hide(context);
context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay)); context.read<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
}, },
onFormatChanged: (format) { onFormatChanged: (format) {
_CalDateTimeSetting.hide(context);
context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format)); context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
}, },
onPageChanged: (focusedDay) { onPageChanged: (focusedDay) {
_CalDateTimeSetting.hide(context);
context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay)); context.read<DateCalBloc>().add(DateCalEvent.setFocusedDay(focusedDay));
}, },
); );
@ -234,6 +237,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
if (widget.bloc.state.dateTypeOption.includeTime) { if (widget.bloc.state.dateTypeOption.includeTime) {
_focusNode.addListener(() { _focusNode.addListener(() {
if (mounted) { if (mounted) {
_CalDateTimeSetting.hide(context);
widget.bloc.add(DateCalEvent.setTime(_controller.text)); widget.bloc.add(DateCalEvent.setTime(_controller.text));
} }
}); });
@ -257,6 +261,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
child: RoundedInputField( child: RoundedInputField(
height: 40, height: 40,
focusNode: _focusNode, focusNode: _focusNode,
hintText: state.timeHintText,
controller: _controller, controller: _controller,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
normalBorderColor: theme.shader4, normalBorderColor: theme.shader4,
@ -326,6 +331,7 @@ class _CalDateTimeSetting extends StatefulWidget {
} }
void show(BuildContext context) { void show(BuildContext context) {
hide(context);
FlowyOverlay.of(context).insertWithAnchor( FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer( widget: OverlayContainer(
child: this, child: this,
@ -337,6 +343,10 @@ class _CalDateTimeSetting extends StatefulWidget {
anchorOffset: const Offset(20, 0), anchorOffset: const Offset(20, 0),
); );
} }
static void hide(BuildContext context) {
FlowyOverlay.of(context).remove(identifier());
}
} }
class _CalDateTimeSettingState extends State<_CalDateTimeSetting> { class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {

View File

@ -42,7 +42,7 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
const YES: &str = "Yes"; const YES: &str = "Yes";
const NO: &str = "No"; const NO: &str = "No";
impl CellDataOperation<String, String> for CheckboxTypeOption { impl CellDataOperation<String> for CheckboxTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,

View File

@ -4,7 +4,7 @@ use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
use bytes::Bytes; use bytes::Bytes;
use chrono::format::strftime::StrftimeItems; use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime; use chrono::{NaiveDateTime, Timelike};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{ use flowy_grid_data_model::entities::{
@ -29,35 +29,36 @@ pub struct DateTypeOption {
impl_type_option!(DateTypeOption, FieldType::DateTime); impl_type_option!(DateTypeOption, FieldType::DateTime);
impl DateTypeOption { impl DateTypeOption {
fn today_desc_from_timestamp(&self, timestamp: i64, time: &Option<String>) -> String {
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
self.today_desc_from_native(native, time)
}
#[allow(dead_code)] #[allow(dead_code)]
fn today_desc_from_str(&self, s: String, time: &Option<String>) -> String { pub fn new() -> Self {
match NaiveDateTime::parse_from_str(&s, &self.date_fmt(time)) { Self::default()
Ok(native) => self.today_desc_from_native(native, time),
Err(_) => "".to_owned(),
}
} }
fn today_desc_from_native(&self, native: chrono::NaiveDateTime, time: &Option<String>) -> String { 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();
}
let time = native.time();
let has_time = time.hour() != 0 || time.second() != 0;
let utc = self.utc_date_time_from_native(native); let utc = self.utc_date_time_from_native(native);
// let china_timezone = FixedOffset::east(8 * 3600); let fmt = self.date_format.format_str();
// let a = utc.with_timezone(&china_timezone); let date = format!("{}", utc.format_with_items(StrftimeItems::new(fmt)));
let fmt = self.date_fmt(time);
let output = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))); let mut time = "".to_string();
output 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_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> { let timestamp = native.timestamp();
let native = NaiveDateTime::from_timestamp(timestamp, 0); DateCellData { date, time, timestamp }
self.utc_date_time_from_native(native)
}
fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime<chrono::Utc> {
chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
} }
fn date_fmt(&self, time: &Option<String>) -> String { fn date_fmt(&self, time: &Option<String>) -> 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( fn timestamp_from_utc_with_time(
&self, &self,
utc: &chrono::DateTime<chrono::Utc>, utc: &chrono::DateTime<chrono::Utc>,
@ -113,9 +106,18 @@ impl DateTypeOption {
Ok(utc.timestamp()) Ok(utc.timestamp())
} }
fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
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::Utc> {
chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
}
} }
impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> for DateTypeOption { impl CellDataOperation<EncodedCellData<DateCellDataSerde>> for DateTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -134,14 +136,11 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
} }
let encoded_data = encoded_data.into().try_into_inner()?; let encoded_data = encoded_data.into().try_into_inner()?;
let date = self.date_desc_from_timestamp(&encoded_data); let date = self.today_desc_from_timestamp(encoded_data.timestamp);
let time = encoded_data.time.unwrap_or_else(|| "".to_owned()); DecodedCellData::try_from_bytes(date)
let timestamp = encoded_data.timestamp;
DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp })
} }
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<DateCellDataSerde, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,
{ {
@ -153,13 +152,13 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
let time = Some(time.trim().to_uppercase()); let time = Some(time.trim().to_uppercase());
let utc = self.utc_date_time_from_timestamp(date_timestamp); let utc = self.utc_date_time_from_timestamp(date_timestamp);
let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; 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)] #[derive(Default, Serialize, Deserialize)]
pub struct DateCellDataSerde { pub(crate) struct DateCellDataSerde {
pub timestamp: i64, pub timestamp: i64,
// #[deprecated(since = "0.0.4", note = "No need to same the time that user input")]
pub time: Option<String>, pub time: Option<String>,
} }
impl DateCellDataSerde { impl DateCellDataSerde {
fn new(timestamp: i64, time: Option<String>, time_format: &TimeFormat) -> Self { pub(crate) fn new(timestamp: i64, _time: Option<String>) -> Self {
Self { Self { timestamp, time: None }
timestamp,
time: Some(time.unwrap_or_else(|| default_time_str(time_format))),
}
}
pub(crate) fn from_timestamp(timestamp: i64, time: Option<String>) -> Self {
Self { timestamp, time }
} }
} }
@ -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)] #[derive(Clone, Debug, Default, ProtoBuf)]
pub struct DateChangesetPayload { pub struct DateChangesetPayload {
#[pb(index = 1)] #[pb(index = 1)]
@ -403,11 +390,11 @@ mod tests {
DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
}; };
use crate::services::row::{CellDataOperation, EncodedCellData}; 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; use strum::IntoEnumIterator;
#[test] #[test]
fn date_description_invalid_input_test() { fn date_type_option_invalid_input_test() {
let type_option = DateTypeOption::default(); let type_option = DateTypeOption::default();
let field_type = FieldType::DateTime; let field_type = FieldType::DateTime;
let field_meta = FieldBuilder::from_field_type(&field_type).build(); let field_meta = FieldBuilder::from_field_type(&field_type).build();
@ -424,7 +411,7 @@ mod tests {
} }
#[test] #[test]
fn date_description_date_format_test() { fn date_type_option_date_format_test() {
let mut type_option = DateTypeOption::default(); let mut type_option = DateTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
for date_format in DateFormat::iter() { for date_format in DateFormat::iter() {
@ -447,7 +434,7 @@ mod tests {
} }
#[test] #[test]
fn date_description_time_format_test() { fn date_type_option_time_format_test() {
let mut type_option = DateTypeOption::default(); let mut type_option = DateTypeOption::default();
let field_type = FieldType::DateTime; let field_type = FieldType::DateTime;
let field_meta = FieldBuilder::from_field_type(&field_type).build(); let field_meta = FieldBuilder::from_field_type(&field_type).build();
@ -465,7 +452,7 @@ mod tests {
}, },
&field_type, &field_type,
&field_meta, &field_meta,
"May 27,2022 00:00", "May 27,2022",
); );
assert_changeset_result( assert_changeset_result(
&type_option, &type_option,
@ -487,9 +474,9 @@ mod tests {
}, },
&field_type, &field_type,
&field_meta, &field_meta,
"May 27,2022 12:00 AM", "May 27,2022",
); );
//
assert_changeset_result( assert_changeset_result(
&type_option, &type_option,
DateCellContentChangeset { DateCellContentChangeset {
@ -517,8 +504,8 @@ mod tests {
} }
#[test] #[test]
fn date_description_apply_changeset_test() { fn date_type_option_apply_changeset_test() {
let mut type_option = DateTypeOption::default(); let mut type_option = DateTypeOption::new();
let field_type = FieldType::DateTime; let field_type = FieldType::DateTime;
let field_meta = FieldBuilder::from_field_type(&field_type).build(); let field_meta = FieldBuilder::from_field_type(&field_type).build();
let date_timestamp = "1653609600".to_owned(); let date_timestamp = "1653609600".to_owned();
@ -543,7 +530,7 @@ mod tests {
}, },
&field_type, &field_type,
&field_meta, &field_meta,
"May 27,2022 00:00", "May 27,2022",
); );
assert_changeset_result( assert_changeset_result(
@ -572,30 +559,53 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn date_description_apply_changeset_error_test() { fn date_type_option_apply_changeset_error_test() {
let mut type_option = DateTypeOption::default(); let mut type_option = DateTypeOption::new();
type_option.include_time = true; 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 date_timestamp = "1653609600".to_owned();
let changeset = DateCellContentChangeset { assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(date_timestamp.clone()), date: Some(date_timestamp.clone()),
time: Some("1:a0".to_owned()),
};
let _ = type_option.apply_changeset(changeset, None).unwrap();
let changeset = DateCellContentChangeset {
date: Some(date_timestamp),
time: Some("1:".to_owned()), time: Some("1:".to_owned()),
}; },
let _ = type_option.apply_changeset(changeset, None).unwrap(); &type_option.field_type(),
&field_meta,
"May 27,2022 01:00",
);
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] #[test]
#[should_panic] #[should_panic]
fn date_description_invalid_data_test() { fn date_type_option_twelve_hours_to_twenty_four_hours() {
let type_option = DateTypeOption::default(); let mut type_option = DateTypeOption::new();
type_option.apply_changeset("he", None).unwrap(); 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( fn assert_changeset_result(
@ -605,7 +615,7 @@ mod tests {
field_meta: &FieldMeta, field_meta: &FieldMeta,
expected: &str, 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!( assert_eq!(
expected.to_owned(), expected.to_owned(),
decode_cell_data(encoded_data, type_option, field_meta) 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) { 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!( assert_eq!(
expected.to_owned(), 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, type_option: &DateTypeOption,
field_meta: &FieldMeta, field_meta: &FieldMeta,
) -> String { ) -> String {
type_option let decoded_data = type_option
.decode_cell_data(encoded_data, &FieldType::DateTime, field_meta) .decode_cell_data(encoded_data, &FieldType::DateTime, field_meta)
.unwrap() .unwrap()
.parse::<DateCellData>() .parse::<DateCellData>()
.unwrap() .unwrap();
.date
if type_option.include_time {
format!("{}{}", decoded_data.date, decoded_data.time)
} else {
format!("{}", decoded_data.date)
}
} }
} }

View File

@ -71,7 +71,7 @@ pub struct NumberTypeOption {
} }
impl_type_option!(NumberTypeOption, FieldType::Number); impl_type_option!(NumberTypeOption, FieldType::Number);
impl CellDataOperation<String, String> for NumberTypeOption { impl CellDataOperation<String> for NumberTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,

View File

@ -95,7 +95,7 @@ impl SelectOptionOperation for SingleSelectTypeOption {
} }
} }
impl CellDataOperation<String, String> for SingleSelectTypeOption { impl CellDataOperation<String> for SingleSelectTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -193,7 +193,7 @@ impl SelectOptionOperation for MultiSelectTypeOption {
} }
} }
impl CellDataOperation<String, String> for MultiSelectTypeOption { impl CellDataOperation<String> for MultiSelectTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,

View File

@ -31,7 +31,7 @@ pub struct RichTextTypeOption {
} }
impl_type_option!(RichTextTypeOption, FieldType::RichText); impl_type_option!(RichTextTypeOption, FieldType::RichText);
impl CellDataOperation<String, String> for RichTextTypeOption { impl CellDataOperation<String> for RichTextTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -80,7 +80,7 @@ mod tests {
// date // date
let field_type = FieldType::DateTime; let field_type = FieldType::DateTime;
let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); 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!( assert_eq!(
type_option type_option
.decode_cell_data(json, &field_type, &date_time_field_meta) .decode_cell_data(json, &field_type, &date_time_field_meta)

View File

@ -34,7 +34,7 @@ pub struct URLTypeOption {
} }
impl_type_option!(URLTypeOption, FieldType::URL); impl_type_option!(URLTypeOption, FieldType::URL);
impl CellDataOperation<EncodedCellData<URLCellData>, String> for URLTypeOption { impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,

View File

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::Formatter; use std::fmt::Formatter;
use std::str::FromStr; use std::str::FromStr;
pub trait CellDataOperation<D, CO: ToString> { pub trait CellDataOperation<ED> {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -14,14 +14,14 @@ pub trait CellDataOperation<D, CO: ToString> {
field_meta: &FieldMeta, field_meta: &FieldMeta,
) -> FlowyResult<DecodedCellData> ) -> FlowyResult<DecodedCellData>
where where
T: Into<D>; T: Into<ED>;
// //
fn apply_changeset<C: Into<CellContentChangeset>>( fn apply_changeset<C: Into<CellContentChangeset>>(
&self, &self,
changeset: C, changeset: C,
cell_meta: Option<CellMeta>, cell_meta: Option<CellMeta>,
) -> FlowyResult<CO>; ) -> FlowyResult<String>;
} }
#[derive(Debug)] #[derive(Debug)]
@ -128,9 +128,7 @@ pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>(
let s = match field_meta.field_type { let s = match field_meta.field_type {
FieldType::RichText => RichTextTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::RichText => RichTextTypeOption::from(field_meta).apply_changeset(changeset, cell_meta),
FieldType::Number => NumberTypeOption::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) FieldType::DateTime => DateTypeOption::from(field_meta).apply_changeset(changeset, cell_meta),
.apply_changeset(changeset, cell_meta)
.map(|data| data.to_string()),
FieldType::SingleSelect => SingleSelectTypeOption::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::MultiSelect => MultiSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta),
FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta),