Selaa lähdekoodia

Merge pull request #525 from AppFlowy-IO/fix/grid_date_time

fix: edit date time issue
Nathan.fooo 2 vuotta sitten
vanhempi
commit
8b40244c73

+ 4 - 0
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"
     }
   }
 }

+ 3 - 1
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<String, String>;
 typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
-typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>;
+typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
 typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
 
 class GridCellContextBuilder {
@@ -31,6 +31,7 @@ class GridCellContextBuilder {
         final cellDataLoader = GridCellDataLoader(
           gridCell: _gridCell,
           parser: DateCellDataParser(),
+          config: const GridCellDataConfig(reloadOnFieldChanged: true),
         );
 
         return GridDateCellContext(
@@ -218,6 +219,7 @@ class _GridCellContext<T, D> extends Equatable {
   }
 
   void dispose() {
+    _cellListener.stop();
     _loadDataOperation?.cancel();
     _saveDataOperation?.cancel();
 

+ 4 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart

@@ -31,18 +31,18 @@ class CellDataPersistence implements _GridCellDataPersistence<String> {
 }
 
 @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<DateCalData> {
+class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> {
   final GridCell gridCell;
   DateCellDataPersistence({
     required this.gridCell,
   });
 
   @override
-  Future<Option<FlowyError>> save(DateCalData data) {
+  Future<Option<FlowyError>> save(CalendarData data) {
     var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell);
 
     final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();

+ 43 - 26
frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart

@@ -38,9 +38,9 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
             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<DateCalEvent, DateCalState> {
             await _updateTypeOption(emit, timeFormat: timeFormat);
           },
           setTime: (time) async {
-            await _updateDateData(emit, time: time);
+            if (state.calData.isSome()) {
+              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}) {
-    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<DateCalEvent, DateCalState> {
     return _saveDateData(emit, newDateData);
   }
 
-  Future<void> _saveDateData(Emitter<DateCalState> emit, DateCalData newDateData) async {
-    if (state.dateData == Some(newDateData)) {
+  Future<void> _saveDateData(Emitter<DateCalState> emit, CalendarData newCalData) async {
+    if (state.calData == Some(newCalData)) {
       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(
-        () => 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<DateCalEvent, DateCalState> {
     );
 
     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<CalendarData> data, Option<String> timeFormatError) =
+      _DidUpdateCalData;
 }
 
 @freezed
@@ -194,36 +199,48 @@ class DateCalState with _$DateCalState {
     required CalendarFormat format,
     required DateTime focusedDay,
     required Option<String> timeFormatError,
-    required Option<DateCalData> dateData,
+    required Option<CalendarData> calData,
     required String? time,
+    required String timeHintText,
   }) = _DateCalState;
 
   factory DateCalState.initial(
     DateTypeOption dateTypeOption,
     DateCellData? cellData,
   ) {
-    Option<DateCalData> dateData = dateDataFromCellData(cellData);
-    final time = dateData.foldRight("", (dateData, previous) => dateData.time);
+    Option<CalendarData> 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<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);
-  Option<DateCalData> dateData = none();
+  Option<CalendarData> 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) {

+ 13 - 13
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<DateCellEvent, DateCellState> {
@@ -17,11 +16,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
         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<DateCellData> data,
+    required DateCellData? data,
+    required String dateStr,
     required Field field,
   }) = _DateCellState;
 
   factory DateCellState.initial(GridDateCellContext context) {
     final cellData = context.getCellData();
-    Option<DateCellData> 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;
+}

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart

@@ -64,7 +64,7 @@ class _DateCellState extends State<DateCell> {
                 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),
                 ),
               ),
             ),

+ 11 - 1
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<DateCalBloc>().add(DateCalEvent.selectDay(selectedDay));
           },
           onFormatChanged: (format) {
+            _CalDateTimeSetting.hide(context);
             context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
           },
           onPageChanged: (focusedDay) {
+            _CalDateTimeSetting.hide(context);
             context.read<DateCalBloc>().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> {

+ 1 - 1
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<String, String> for CheckboxTypeOption {
+impl CellDataOperation<String> for CheckboxTypeOption {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,

+ 115 - 123
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;
+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
@@ -29,35 +28,36 @@ pub struct DateTypeOption {
 impl_type_option!(DateTypeOption, FieldType::DateTime);
 
 impl DateTypeOption {
-    fn today_desc_from_timestamp(&self, timestamp: i64, time: &Option<String>) -> String {
+    #[allow(dead_code)]
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    fn today_desc_from_timestamp(&self, timestamp: i64) -> DateCellData {
         let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
-        self.today_desc_from_native(native, time)
+        self.date_from_native(native)
     }
 
-    #[allow(dead_code)]
-    fn today_desc_from_str(&self, s: String, time: &Option<String>) -> String {
-        match NaiveDateTime::parse_from_str(&s, &self.date_fmt(time)) {
-            Ok(native) => self.today_desc_from_native(native, time),
-            Err(_) => "".to_owned(),
+    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>) -> 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<chrono::Utc> {
-        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::Utc> {
-        chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
+        let timestamp = native.timestamp();
+        DateCellData { date, time, timestamp }
     }
 
     fn date_fmt(&self, time: &Option<String>) -> String {
@@ -77,14 +77,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<chrono::Utc>,
@@ -113,9 +105,18 @@ impl DateTypeOption {
 
         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<String> for DateTypeOption {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,
@@ -123,7 +124,7 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
         _field_meta: &FieldMeta,
     ) -> FlowyResult<DecodedCellData>
     where
-        T: Into<EncodedCellData<DateCellDataSerde>>,
+        T: Into<String>,
     {
         // Return default data if the type_option_cell_data is not FieldType::DateTime.
         // It happens when switching from one field to another.
@@ -133,33 +134,30 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
             return Ok(DecodedCellData::default());
         }
 
-        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 timestamp = encoded_data.into().parse::<i64>().unwrap_or(0);
+        let date = self.today_desc_from_timestamp(timestamp);
+        DecodedCellData::try_from_bytes(date)
     }
 
-    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
         C: Into<CellContentChangeset>,
     {
         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, &self.time_format)
+                    timestamp
                 }
-                _ => DateCellDataSerde::from_timestamp(date_timestamp, Some(default_time_str(&self.time_format))),
+                _ => date_timestamp,
             },
         };
 
-        Ok(cell_data)
+        Ok(cell_data.to_string())
     }
 }
 
@@ -283,46 +281,6 @@ pub struct DateCellData {
     pub timestamp: i64,
 }
 
-#[derive(Default, Serialize, Deserialize)]
-pub struct DateCellDataSerde {
-    pub timestamp: i64,
-    pub time: Option<String>,
-}
-
-impl DateCellDataSerde {
-    fn new(timestamp: i64, time: Option<String>, 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<String>) -> Self {
-        Self { timestamp, time }
-    }
-}
-
-impl FromStr for DateCellDataSerde {
-    type Err = FlowyError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        serde_json::from_str::<DateCellDataSerde>(s).map_err(internal_error)
-    }
-}
-
-impl ToString for DateCellDataSerde {
-    fn to_string(&self) -> String {
-        serde_json::to_string(&self).unwrap_or_else(|_| "".to_string())
-    }
-}
-
-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)]
@@ -399,15 +357,13 @@ impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
 #[cfg(test)]
 mod tests {
     use crate::services::field::FieldBuilder;
-    use crate::services::field::{
-        DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
-    };
-    use crate::services::row::{CellDataOperation, EncodedCellData};
-    use flowy_grid_data_model::entities::{FieldMeta, FieldType};
+    use crate::services::field::{DateCellContentChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
+    use crate::services::row::CellDataOperation;
+    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 +380,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 +403,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 +421,7 @@ mod tests {
                         },
                         &field_type,
                         &field_meta,
-                        "May 27,2022 00:00",
+                        "May 27,2022",
                     );
                     assert_changeset_result(
                         &type_option,
@@ -487,9 +443,9 @@ mod tests {
                         },
                         &field_type,
                         &field_meta,
-                        "May 27,2022 12:00 AM",
+                        "May 27,2022",
                     );
-
+                    //
                     assert_changeset_result(
                         &type_option,
                         DateCellContentChangeset {
@@ -517,8 +473,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 +499,7 @@ mod tests {
             },
             &field_type,
             &field_meta,
-            "May 27,2022 00:00",
+            "May 27,2022",
         );
 
         assert_changeset_result(
@@ -572,30 +528,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 +584,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,24 +592,37 @@ 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)
         );
     }
 
-    fn decode_cell_data<T: Into<EncodedCellData<DateCellDataSerde>>>(
+    fn decode_cell_data<T: Into<String>>(
         encoded_data: T,
         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::<DateCellData>()
-            .unwrap()
-            .date
+            .unwrap();
+
+        if type_option.include_time {
+            format!("{}{}", decoded_data.date, decoded_data.time)
+        } else {
+            format!("{}", decoded_data.date)
+        }
     }
 }

+ 1 - 1
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<String, String> for NumberTypeOption {
+impl CellDataOperation<String> for NumberTypeOption {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

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

+ 3 - 3
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<String, String> for RichTextTypeOption {
+impl CellDataOperation<String> for RichTextTypeOption {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,
@@ -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::from_timestamp(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::<DateCellData>()
                 .unwrap()

+ 1 - 1
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<EncodedCellData<URLCellData>, String> for URLTypeOption {
+impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,

+ 4 - 6
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<D, CO: ToString> {
+pub trait CellDataOperation<ED> {
     fn decode_cell_data<T>(
         &self,
         encoded_data: T,
@@ -14,14 +14,14 @@ pub trait CellDataOperation<D, CO: ToString> {
         field_meta: &FieldMeta,
     ) -> FlowyResult<DecodedCellData>
     where
-        T: Into<D>;
+        T: Into<ED>;
 
     //
     fn apply_changeset<C: Into<CellContentChangeset>>(
         &self,
         changeset: C,
         cell_meta: Option<CellMeta>,
-    ) -> FlowyResult<CO>;
+    ) -> FlowyResult<String>;
 }
 
 #[derive(Debug)]
@@ -128,9 +128,7 @@ pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>(
     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),