Browse Source

Merge pull request #1452 from AppFlowy-IO/feat/filter_date

Feat/filter date
Nathan.fooo 2 years ago
parent
commit
c3a41ba9ad
45 changed files with 523 additions and 383 deletions
  1. 2 2
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart
  2. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_data_loader.dart
  3. 8 5
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_data_persistence.dart
  4. 2 2
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart
  5. 9 9
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart
  6. 3 3
      frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart
  7. 2 2
      frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart
  8. 4 4
      frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart
  9. 2 2
      frontend/app_flowy/lib/plugins/grid/application/field/grid_listener.dart
  10. 7 7
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart
  11. 2 2
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart
  12. 1 1
      frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
  13. 1 1
      frontend/app_flowy/lib/plugins/grid/application/prelude.dart
  14. 1 1
      frontend/app_flowy/lib/plugins/grid/application/setting/setting_service.dart
  15. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
  16. 1 0
      frontend/app_flowy/test/bloc_test/board_test/group_by_field_test.dart
  17. 11 11
      frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs
  18. 25 37
      frontend/rust-lib/flowy-grid/src/entities/field_entities.rs
  19. 12 55
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs
  20. 2 2
      frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs
  21. 39 30
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  22. 23 23
      frontend/rust-lib/flowy-grid/src/event_map.rs
  23. 10 9
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  24. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs
  25. 86 31
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs
  26. 28 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs
  27. 9 16
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs
  28. 24 50
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  29. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  30. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs
  31. 66 21
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs
  32. 11 11
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs
  33. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs
  34. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs
  35. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs
  36. 28 11
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  37. 2 2
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  38. 1 1
      frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
  39. 3 3
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  40. 3 2
      frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs
  41. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs
  42. 2 1
      frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs
  43. 64 3
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs
  44. 11 4
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  45. 2 2
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart

@@ -234,7 +234,7 @@ class IGridCellController<T, D> extends Equatable {
     return data;
   }
 
-  /// Return the FieldTypeOptionDataPB that can be parsed into corresponding class using the [parser].
+  /// Return the TypeOptionPB that can be parsed into corresponding class using the [parser].
   /// [PD] is the type that the parser return.
   Future<Either<PD, FlowyError>>
       getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
@@ -329,7 +329,7 @@ class GridCellFieldNotifierImpl extends IGridCellFieldNotifier {
 
   @override
   void onCellFieldChanged(void Function(FieldPB p1) callback) {
-    _onChangesetFn = (FieldChangesetPB changeset) {
+    _onChangesetFn = (GridFieldChangesetPB changeset) {
       for (final updatedField in changeset.updatedFields) {
         callback(updatedField);
       }

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_data_loader.dart

@@ -25,7 +25,7 @@ class GridCellDataLoader<T> {
     final fut = service.getCell(cellId: cellId);
     return fut.then(
       (result) => result.fold(
-        (GridCellPB cell) {
+        (CellPB cell) {
           try {
             return parser.parserData(cell.data);
           } catch (e, s) {

+ 8 - 5
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_data_persistence.dart

@@ -29,10 +29,12 @@ class CellDataPersistence implements IGridCellDataPersistence<String> {
 
 @freezed
 class CalendarData with _$CalendarData {
-  const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
+  const factory CalendarData({required DateTime date, String? time}) =
+      _CalendarData;
 }
 
-class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
+class DateCellDataPersistence
+    implements IGridCellDataPersistence<CalendarData> {
   final GridCellIdentifier cellId;
   DateCellDataPersistence({
     required this.cellId,
@@ -40,10 +42,11 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
 
   @override
   Future<Option<FlowyError>> save(CalendarData data) {
-    var payload = DateChangesetPayloadPB.create()..cellIdentifier = _makeCellIdPayload(cellId);
+    var payload = DateChangesetPB.create()..cellPath = _makeCellPath(cellId);
 
     final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
     payload.date = date;
+    payload.isUtc = data.date.isUtc;
 
     if (data.time != null) {
       payload.time = data.time!;
@@ -58,8 +61,8 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
   }
 }
 
-GridCellIdPB _makeCellIdPayload(GridCellIdentifier cellId) {
-  return GridCellIdPB.create()
+CellPathPB _makeCellPath(GridCellIdentifier cellId) {
+  return CellPathPB.create()
     ..gridId = cellId.gridId
     ..fieldId = cellId.fieldId
     ..rowId = cellId.rowId;

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart

@@ -42,10 +42,10 @@ class CellService {
     return GridEventUpdateCell(payload).send();
   }
 
-  Future<Either<GridCellPB, FlowyError>> getCell({
+  Future<Either<CellPB, FlowyError>> getCell({
     required GridCellIdentifier cellId,
   }) {
-    final payload = GridCellIdPB.create()
+    final payload = CellPathPB.create()
       ..gridId = cellId.gridId
       ..fieldId = cellId.fieldId
       ..rowId = cellId.rowId;

+ 9 - 9
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart

@@ -21,11 +21,11 @@ class SelectOptionService {
       (result) {
         return result.fold(
           (option) {
-            final cellIdentifier = GridCellIdPB.create()
+            final cellIdentifier = CellPathPB.create()
               ..gridId = gridId
               ..fieldId = fieldId
               ..rowId = rowId;
-            final payload = SelectOptionChangesetPayloadPB.create()
+            final payload = SelectOptionChangesetPB.create()
               ..insertOptions.add(option)
               ..cellIdentifier = cellIdentifier;
             return GridEventUpdateSelectOption(payload).send();
@@ -39,7 +39,7 @@ class SelectOptionService {
   Future<Either<Unit, FlowyError>> update({
     required SelectOptionPB option,
   }) {
-    final payload = SelectOptionChangesetPayloadPB.create()
+    final payload = SelectOptionChangesetPB.create()
       ..updateOptions.add(option)
       ..cellIdentifier = _cellIdentifier();
     return GridEventUpdateSelectOption(payload).send();
@@ -47,7 +47,7 @@ class SelectOptionService {
 
   Future<Either<Unit, FlowyError>> delete(
       {required Iterable<SelectOptionPB> options}) {
-    final payload = SelectOptionChangesetPayloadPB.create()
+    final payload = SelectOptionChangesetPB.create()
       ..deleteOptions.addAll(options)
       ..cellIdentifier = _cellIdentifier();
 
@@ -55,7 +55,7 @@ class SelectOptionService {
   }
 
   Future<Either<SelectOptionCellDataPB, FlowyError>> getOptionContext() {
-    final payload = GridCellIdPB.create()
+    final payload = CellPathPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..rowId = rowId;
@@ -65,7 +65,7 @@ class SelectOptionService {
 
   Future<Either<void, FlowyError>> select(
       {required Iterable<String> optionIds}) {
-    final payload = SelectOptionCellChangesetPayloadPB.create()
+    final payload = SelectOptionCellChangesetPB.create()
       ..cellIdentifier = _cellIdentifier()
       ..insertOptionIds.addAll(optionIds);
     return GridEventUpdateSelectOptionCell(payload).send();
@@ -73,14 +73,14 @@ class SelectOptionService {
 
   Future<Either<void, FlowyError>> unSelect(
       {required Iterable<String> optionIds}) {
-    final payload = SelectOptionCellChangesetPayloadPB.create()
+    final payload = SelectOptionCellChangesetPB.create()
       ..cellIdentifier = _cellIdentifier()
       ..deleteOptionIds.addAll(optionIds);
     return GridEventUpdateSelectOptionCell(payload).send();
   }
 
-  GridCellIdPB _cellIdentifier() {
-    return GridCellIdPB.create()
+  CellPathPB _cellIdentifier() {
+    return CellPathPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..rowId = rowId;

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/application/field/field_action_sheet_bloc.dart

@@ -18,7 +18,7 @@ class FieldActionSheetBloc
         ),
         super(
           FieldActionSheetState.initial(
-            FieldTypeOptionDataPB.create()..field_2 = fieldCellContext.field,
+            TypeOptionPB.create()..field_2 = fieldCellContext.field,
           ),
         ) {
     on<FieldActionSheetEvent>(
@@ -85,12 +85,12 @@ class FieldActionSheetEvent with _$FieldActionSheetEvent {
 @freezed
 class FieldActionSheetState with _$FieldActionSheetState {
   const factory FieldActionSheetState({
-    required FieldTypeOptionDataPB fieldTypeOptionData,
+    required TypeOptionPB fieldTypeOptionData,
     required String errorText,
     required String fieldName,
   }) = _FieldActionSheetState;
 
-  factory FieldActionSheetState.initial(FieldTypeOptionDataPB data) =>
+  factory FieldActionSheetState.initial(TypeOptionPB data) =>
       FieldActionSheetState(
         fieldTypeOptionData: data,
         errorText: '',

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart

@@ -27,7 +27,7 @@ class _GridFieldNotifier extends ChangeNotifier {
   List<GridFieldContext> get fieldContexts => _fieldContexts;
 }
 
-typedef OnChangeset = void Function(FieldChangesetPB);
+typedef OnChangeset = void Function(GridFieldChangesetPB);
 typedef OnReceiveFields = void Function(List<GridFieldContext>);
 
 class GridFieldController {
@@ -247,7 +247,7 @@ class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
 
   @override
   void onRowFieldChanged(void Function(FieldPB) callback) {
-    _onChangesetFn = (FieldChangesetPB changeset) {
+    _onChangesetFn = (GridFieldChangesetPB changeset) {
       for (final updatedField in changeset.updatedFields) {
         callback(updatedField);
       }

+ 4 - 4
frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart

@@ -36,7 +36,7 @@ class FieldService {
     double? width,
     List<int>? typeOptionData,
   }) {
-    var payload = FieldChangesetPayloadPB.create()
+    var payload = FieldChangesetPB.create()
       ..gridId = gridId
       ..fieldId = fieldId;
 
@@ -72,7 +72,7 @@ class FieldService {
     required String fieldId,
     required List<int> typeOptionData,
   }) {
-    var payload = UpdateFieldTypeOptionPayloadPB.create()
+    var payload = TypeOptionChangesetPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..typeOptionData = typeOptionData;
@@ -96,10 +96,10 @@ class FieldService {
     return GridEventDuplicateField(payload).send();
   }
 
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> getFieldTypeOptionData({
+  Future<Either<TypeOptionPB, FlowyError>> getFieldTypeOptionData({
     required FieldType fieldType,
   }) {
-    final payload = FieldTypeOptionIdPB.create()
+    final payload = TypeOptionPathPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..fieldType = fieldType;

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/field/grid_listener.dart

@@ -7,7 +7,7 @@ import 'dart:async';
 import 'dart:typed_data';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 
-typedef UpdateFieldNotifiedValue = Either<FieldChangesetPB, FlowyError>;
+typedef UpdateFieldNotifiedValue = Either<GridFieldChangesetPB, FlowyError>;
 
 class GridFieldsListener {
   final String gridId;
@@ -30,7 +30,7 @@ class GridFieldsListener {
       case GridNotification.DidUpdateGridField:
         result.fold(
           (payload) => updateFieldsNotifier?.value =
-              left(FieldChangesetPB.fromBuffer(payload)),
+              left(GridFieldChangesetPB.fromBuffer(payload)),
           (error) => updateFieldsNotifier?.value = right(error),
         );
         break;

+ 7 - 7
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart

@@ -143,11 +143,11 @@ abstract class TypeOptionFieldDelegate {
 
 abstract class IFieldTypeOptionLoader {
   String get gridId;
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load();
+  Future<Either<TypeOptionPB, FlowyError>> load();
 
   Future<Either<Unit, FlowyError>> switchToField(
       String fieldId, FieldType fieldType) {
-    final payload = EditFieldPayloadPB.create()
+    final payload = EditFieldChangesetPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..fieldType = fieldType;
@@ -158,7 +158,7 @@ abstract class IFieldTypeOptionLoader {
 
 /// Uses when creating a new field
 class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
-  FieldTypeOptionDataPB? fieldTypeOption;
+  TypeOptionPB? fieldTypeOption;
 
   @override
   final String gridId;
@@ -169,9 +169,9 @@ class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
   /// Creates the field type option if the fieldTypeOption is null.
   /// Otherwise, it loads the type option data from the backend.
   @override
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
+  Future<Either<TypeOptionPB, FlowyError>> load() {
     if (fieldTypeOption != null) {
-      final payload = FieldTypeOptionIdPB.create()
+      final payload = TypeOptionPathPB.create()
         ..gridId = gridId
         ..fieldId = fieldTypeOption!.field_2.id
         ..fieldType = fieldTypeOption!.field_2.fieldType;
@@ -207,8 +207,8 @@ class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
   });
 
   @override
-  Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
-    final payload = FieldTypeOptionIdPB.create()
+  Future<Either<TypeOptionPB, FlowyError>> load() {
+    final payload = TypeOptionPathPB.create()
       ..gridId = gridId
       ..fieldId = field.id
       ..fieldType = field.fieldType;

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart

@@ -12,7 +12,7 @@ import 'type_option_context.dart';
 class TypeOptionDataController {
   final String gridId;
   final IFieldTypeOptionLoader loader;
-  late FieldTypeOptionDataPB _data;
+  late TypeOptionPB _data;
   final PublishNotifier<FieldPB> _fieldNotifier = PublishNotifier();
 
   /// Returns a [TypeOptionDataController] used to modify the specified
@@ -27,7 +27,7 @@ class TypeOptionDataController {
     GridFieldContext? fieldContext,
   }) {
     if (fieldContext != null) {
-      _data = FieldTypeOptionDataPB.create()
+      _data = TypeOptionPB.create()
         ..gridId = gridId
         ..field_2 = fieldContext.field;
     }

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/grid_service.dart

@@ -44,7 +44,7 @@ class GridFFIService {
 
   Future<Either<RepeatedFieldPB, FlowyError>> getFields(
       {required List<FieldIdPB> fieldIds}) {
-    final payload = QueryFieldPayloadPB.create()
+    final payload = GetFieldPayloadPB.create()
       ..gridId = gridId
       ..fieldIds = RepeatedFieldIdPB(items: fieldIds);
     return GridEventGetFields(payload).send();

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/prelude.dart

@@ -15,7 +15,7 @@ export 'field/type_option/date_bloc.dart';
 export 'field/type_option/number_bloc.dart';
 export 'field/type_option/single_select_type_option.dart';
 
-// GridCellPB
+// CellPB
 export 'cell/text_cell_bloc.dart';
 export 'cell/number_cell_bloc.dart';
 export 'cell/select_option_cell_bloc.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/setting/setting_service.dart

@@ -23,7 +23,7 @@ class SettingFFIService {
     final insertGroupPayload = InsertGroupPayloadPB.create()
       ..fieldId = fieldId
       ..fieldType = fieldType;
-    final payload = GridSettingChangesetPayloadPB.create()
+    final payload = GridSettingChangesetPB.create()
       ..gridId = viewId
       ..insertGroup = insertGroupPayload;
 

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart

@@ -17,8 +17,8 @@ import 'field_type_list.dart';
 import 'type_option/builder.dart';
 
 typedef UpdateFieldCallback = void Function(FieldPB, Uint8List);
-typedef SwitchToFieldCallback
-    = Future<Either<FieldTypeOptionDataPB, FlowyError>> Function(
+typedef SwitchToFieldCallback = Future<Either<TypeOptionPB, FlowyError>>
+    Function(
   String fieldId,
   FieldType fieldType,
 );

+ 1 - 0
frontend/app_flowy/test/bloc_test/board_test/group_by_field_test.dart

@@ -105,6 +105,7 @@ void main() {
         fieldController: boardTest.context.fieldController,
       ),
       act: (bloc) async {
+        await boardResponseFuture();
         bloc.add(GridGroupEvent.setGroupByField(
           multiSelectField.id,
           multiSelectField.fieldType,

+ 11 - 11
frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs

@@ -39,7 +39,7 @@ impl TryInto<CreateSelectOptionParams> for CreateSelectOptionPayloadPB {
 }
 
 #[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct GridCellIdPB {
+pub struct CellPathPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -50,20 +50,20 @@ pub struct GridCellIdPB {
     pub row_id: String,
 }
 
-pub struct GridCellIdParams {
+pub struct CellPathParams {
     pub grid_id: String,
     pub field_id: String,
     pub row_id: String,
 }
 
-impl TryInto<GridCellIdParams> for GridCellIdPB {
+impl TryInto<CellPathParams> for CellPathPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<GridCellIdParams, Self::Error> {
+    fn try_into(self) -> Result<CellPathParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
         let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
-        Ok(GridCellIdParams {
+        Ok(CellPathParams {
             grid_id: grid_id.0,
             field_id: field_id.0,
             row_id: row_id.0,
@@ -71,7 +71,7 @@ impl TryInto<GridCellIdParams> for GridCellIdPB {
     }
 }
 #[derive(Debug, Default, ProtoBuf)]
-pub struct GridCellPB {
+pub struct CellPB {
     #[pb(index = 1)]
     pub field_id: String,
 
@@ -83,7 +83,7 @@ pub struct GridCellPB {
     pub field_type: Option<FieldType>,
 }
 
-impl GridCellPB {
+impl CellPB {
     pub fn new(field_id: &str, field_type: FieldType, data: Vec<u8>) -> Self {
         Self {
             field_id: field_id.to_owned(),
@@ -104,11 +104,11 @@ impl GridCellPB {
 #[derive(Debug, Default, ProtoBuf)]
 pub struct RepeatedCellPB {
     #[pb(index = 1)]
-    pub items: Vec<GridCellPB>,
+    pub items: Vec<CellPB>,
 }
 
 impl std::ops::Deref for RepeatedCellPB {
-    type Target = Vec<GridCellPB>;
+    type Target = Vec<CellPB>;
     fn deref(&self) -> &Self::Target {
         &self.items
     }
@@ -120,8 +120,8 @@ impl std::ops::DerefMut for RepeatedCellPB {
     }
 }
 
-impl std::convert::From<Vec<GridCellPB>> for RepeatedCellPB {
-    fn from(items: Vec<GridCellPB>) -> Self {
+impl std::convert::From<Vec<CellPB>> for RepeatedCellPB {
+    fn from(items: Vec<CellPB>) -> Self {
         Self { items }
     }
 }

+ 25 - 37
frontend/rust-lib/flowy-grid/src/entities/field_entities.rs

@@ -84,7 +84,7 @@ impl std::convert::From<&Arc<FieldRevision>> for FieldIdPB {
     }
 }
 #[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct FieldChangesetPB {
+pub struct GridFieldChangesetPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -98,7 +98,7 @@ pub struct FieldChangesetPB {
     pub updated_fields: Vec<FieldPB>,
 }
 
-impl FieldChangesetPB {
+impl GridFieldChangesetPB {
     pub fn insert(grid_id: &str, inserted_fields: Vec<IndexFieldPB>) -> Self {
         Self {
             grid_id: grid_id.to_owned(),
@@ -145,18 +145,6 @@ impl IndexFieldPB {
     }
 }
 
-#[derive(Debug, Default, ProtoBuf)]
-pub struct GetEditFieldContextPayloadPB {
-    #[pb(index = 1)]
-    pub grid_id: String,
-
-    #[pb(index = 2, one_of)]
-    pub field_id: Option<String>,
-
-    #[pb(index = 3)]
-    pub field_type: FieldType,
-}
-
 #[derive(Debug, Default, ProtoBuf)]
 pub struct CreateFieldPayloadPB {
     #[pb(index = 1)]
@@ -190,7 +178,7 @@ impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct EditFieldPayloadPB {
+pub struct EditFieldChangesetPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -210,7 +198,7 @@ pub struct EditFieldParams {
     pub field_type: FieldType,
 }
 
-impl TryInto<EditFieldParams> for EditFieldPayloadPB {
+impl TryInto<EditFieldParams> for EditFieldChangesetPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<EditFieldParams, Self::Error> {
@@ -225,7 +213,7 @@ impl TryInto<EditFieldParams> for EditFieldPayloadPB {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct FieldTypeOptionIdPB {
+pub struct TypeOptionPathPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -236,19 +224,19 @@ pub struct FieldTypeOptionIdPB {
     pub field_type: FieldType,
 }
 
-pub struct FieldTypeOptionIdParams {
+pub struct TypeOptionPathParams {
     pub grid_id: String,
     pub field_id: String,
     pub field_type: FieldType,
 }
 
-impl TryInto<FieldTypeOptionIdParams> for FieldTypeOptionIdPB {
+impl TryInto<TypeOptionPathParams> for TypeOptionPathPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<FieldTypeOptionIdParams, Self::Error> {
+    fn try_into(self) -> Result<TypeOptionPathParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
-        Ok(FieldTypeOptionIdParams {
+        Ok(TypeOptionPathParams {
             grid_id: grid_id.0,
             field_id: field_id.0,
             field_type: self.field_type,
@@ -257,7 +245,7 @@ impl TryInto<FieldTypeOptionIdParams> for FieldTypeOptionIdPB {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct FieldTypeOptionDataPB {
+pub struct TypeOptionPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -320,35 +308,35 @@ impl std::convert::From<String> for RepeatedFieldIdPB {
     }
 }
 
-/// [UpdateFieldTypeOptionPayloadPB] is used to update the type-option data.
+/// [TypeOptionChangesetPB] is used to update the type-option data.
 #[derive(ProtoBuf, Default)]
-pub struct UpdateFieldTypeOptionPayloadPB {
+pub struct TypeOptionChangesetPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
     #[pb(index = 2)]
     pub field_id: String,
 
-    /// Check out [FieldTypeOptionDataPB] for more details.
+    /// Check out [TypeOptionPB] for more details.
     #[pb(index = 3)]
     pub type_option_data: Vec<u8>,
 }
 
 #[derive(Clone)]
-pub struct UpdateFieldTypeOptionParams {
+pub struct TypeOptionChangesetParams {
     pub grid_id: String,
     pub field_id: String,
     pub type_option_data: Vec<u8>,
 }
 
-impl TryInto<UpdateFieldTypeOptionParams> for UpdateFieldTypeOptionPayloadPB {
+impl TryInto<TypeOptionChangesetParams> for TypeOptionChangesetPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<UpdateFieldTypeOptionParams, Self::Error> {
+    fn try_into(self) -> Result<TypeOptionChangesetParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let _ = NotEmptyStr::parse(self.field_id.clone()).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
 
-        Ok(UpdateFieldTypeOptionParams {
+        Ok(TypeOptionChangesetParams {
             grid_id: grid_id.0,
             field_id: self.field_id,
             type_option_data: self.type_option_data,
@@ -357,7 +345,7 @@ impl TryInto<UpdateFieldTypeOptionParams> for UpdateFieldTypeOptionPayloadPB {
 }
 
 #[derive(ProtoBuf, Default)]
-pub struct QueryFieldPayloadPB {
+pub struct GetFieldPayloadPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -365,31 +353,31 @@ pub struct QueryFieldPayloadPB {
     pub field_ids: RepeatedFieldIdPB,
 }
 
-pub struct QueryFieldParams {
+pub struct GetFieldParams {
     pub grid_id: String,
     pub field_ids: RepeatedFieldIdPB,
 }
 
-impl TryInto<QueryFieldParams> for QueryFieldPayloadPB {
+impl TryInto<GetFieldParams> for GetFieldPayloadPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<QueryFieldParams, Self::Error> {
+    fn try_into(self) -> Result<GetFieldParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        Ok(QueryFieldParams {
+        Ok(GetFieldParams {
             grid_id: grid_id.0,
             field_ids: self.field_ids,
         })
     }
 }
 
-/// [FieldChangesetPayloadPB] is used to modify the corresponding field. It defines which properties of
+/// [FieldChangesetPB] is used to modify the corresponding field. It defines which properties of
 /// the field can be modified.
 ///
 /// Pass in None if you don't want to modify a property
 /// Pass in Some(Value) if you want to modify a property
 ///
 #[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct FieldChangesetPayloadPB {
+pub struct FieldChangesetPB {
     #[pb(index = 1)]
     pub field_id: String,
 
@@ -418,7 +406,7 @@ pub struct FieldChangesetPayloadPB {
     pub type_option_data: Option<Vec<u8>>,
 }
 
-impl TryInto<FieldChangesetParams> for FieldChangesetPayloadPB {
+impl TryInto<FieldChangesetParams> for FieldChangesetPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<FieldChangesetParams, Self::Error> {

+ 12 - 55
frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs

@@ -1,5 +1,3 @@
-use crate::entities::parser::NotEmptyStr;
-use crate::entities::FieldType;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use grid_rev_model::FilterRevision;
@@ -17,68 +15,25 @@ pub struct DateFilterPB {
 
     #[pb(index = 3, one_of)]
     pub end: Option<i64>,
-}
-
-#[derive(ProtoBuf, Default, Clone, Debug)]
-pub struct CreateGridDateFilterPayload {
-    #[pb(index = 1)]
-    pub field_id: String,
-
-    #[pb(index = 2)]
-    pub field_type: FieldType,
-
-    #[pb(index = 3)]
-    pub condition: DateFilterCondition,
 
     #[pb(index = 4, one_of)]
-    pub start: Option<i64>,
-
-    #[pb(index = 5, one_of)]
-    pub end: Option<i64>,
+    pub timestamp: Option<i64>,
 }
 
-pub struct CreateGridDateFilterParams {
-    pub field_id: String,
-
-    pub field_type: FieldType,
-
-    pub condition: DateFilterCondition,
-
+#[derive(Deserialize, Serialize, Default, Clone, Debug)]
+pub struct DateFilterContent {
     pub start: Option<i64>,
-
     pub end: Option<i64>,
+    pub timestamp: Option<i64>,
 }
 
-impl TryInto<CreateGridDateFilterParams> for CreateGridDateFilterPayload {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<CreateGridDateFilterParams, Self::Error> {
-        let field_id = NotEmptyStr::parse(self.field_id)
-            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
-            .0;
-        Ok(CreateGridDateFilterParams {
-            field_id,
-            condition: self.condition,
-            start: self.start,
-            field_type: self.field_type,
-            end: self.end,
-        })
-    }
-}
-
-#[derive(Serialize, Deserialize, Default)]
-struct DateRange {
-    start: Option<i64>,
-    end: Option<i64>,
-}
-
-impl ToString for DateRange {
+impl ToString for DateFilterContent {
     fn to_string(&self) -> String {
-        serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
+        serde_json::to_string(self).unwrap()
     }
 }
 
-impl FromStr for DateRange {
+impl FromStr for DateFilterContent {
     type Err = serde_json::Error;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -96,6 +51,7 @@ pub enum DateFilterCondition {
     DateOnOrAfter = 4,
     DateWithIn = 5,
     DateIsEmpty = 6,
+    DateIsNotEmpty = 7,
 }
 
 impl std::convert::From<DateFilterCondition> for u32 {
@@ -133,9 +89,10 @@ impl std::convert::From<Arc<FilterRevision>> for DateFilterPB {
             ..Default::default()
         };
 
-        if let Ok(range) = DateRange::from_str(&rev.content) {
-            filter.start = range.start;
-            filter.end = range.end;
+        if let Ok(content) = DateFilterContent::from_str(&rev.content) {
+            filter.start = content.start;
+            filter.end = content.end;
+            filter.timestamp = content.timestamp;
         };
 
         filter

+ 2 - 2
frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs

@@ -76,7 +76,7 @@ impl std::convert::From<GridLayout> for LayoutRevision {
 }
 
 #[derive(Default, ProtoBuf)]
-pub struct GridSettingChangesetPayloadPB {
+pub struct GridSettingChangesetPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -96,7 +96,7 @@ pub struct GridSettingChangesetPayloadPB {
     pub delete_group: Option<DeleteGroupPayloadPB>,
 }
 
-impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
+impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<GridSettingChangesetParams, Self::Error> {

+ 39 - 30
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -3,8 +3,8 @@ use crate::manager::GridManager;
 use crate::services::cell::AnyCellData;
 use crate::services::field::{
     default_type_option_builder_from_type, select_type_option_from_field_rev, type_option_builder_from_json_str,
-    DateChangesetParams, DateChangesetPayloadPB, SelectOptionCellChangeset, SelectOptionCellChangesetParams,
-    SelectOptionCellChangesetPayloadPB, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPayloadPB,
+    DateCellChangeset, DateChangesetPB, SelectOptionCellChangeset, SelectOptionCellChangesetPB,
+    SelectOptionCellChangesetParams, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPB,
     SelectOptionPB,
 };
 use crate::services::row::{make_block_pbs, make_row_from_row_rev};
@@ -37,7 +37,7 @@ pub(crate) async fn get_grid_setting_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn update_grid_setting_handler(
-    data: Data<GridSettingChangesetPayloadPB>,
+    data: Data<GridSettingChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let params: GridSettingChangesetParams = data.into_inner().try_into()?;
@@ -74,10 +74,10 @@ pub(crate) async fn get_grid_blocks_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_fields_handler(
-    data: Data<QueryFieldPayloadPB>,
+    data: Data<GetFieldPayloadPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RepeatedFieldPB, FlowyError> {
-    let params: QueryFieldParams = data.into_inner().try_into()?;
+    let params: GetFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     let field_orders = params
         .field_ids
@@ -92,7 +92,7 @@ pub(crate) async fn get_fields_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn update_field_handler(
-    data: Data<FieldChangesetPayloadPB>,
+    data: Data<FieldChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let changeset: FieldChangesetParams = data.into_inner().try_into()?;
@@ -103,10 +103,10 @@ pub(crate) async fn update_field_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn update_field_type_option_handler(
-    data: Data<UpdateFieldTypeOptionPayloadPB>,
+    data: Data<TypeOptionChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: UpdateFieldTypeOptionParams = data.into_inner().try_into()?;
+    let params: TypeOptionChangesetParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     let _ = editor
         .update_field_type_option(&params.grid_id, &params.field_id, params.type_option_data)
@@ -127,7 +127,7 @@ pub(crate) async fn delete_field_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn switch_to_field_handler(
-    data: Data<EditFieldPayloadPB>,
+    data: Data<EditFieldChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let params: EditFieldParams = data.into_inner().try_into()?;
@@ -165,17 +165,17 @@ pub(crate) async fn duplicate_field_handler(
 /// Return the FieldTypeOptionData if the Field exists otherwise return record not found error.
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_field_type_option_data_handler(
-    data: Data<FieldTypeOptionIdPB>,
+    data: Data<TypeOptionPathPB>,
     manager: AppData<Arc<GridManager>>,
-) -> DataResult<FieldTypeOptionDataPB, FlowyError> {
-    let params: FieldTypeOptionIdParams = data.into_inner().try_into()?;
+) -> DataResult<TypeOptionPB, FlowyError> {
+    let params: TypeOptionPathParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     match editor.get_field_rev(&params.field_id).await {
         None => Err(FlowyError::record_not_found()),
         Some(field_rev) => {
             let field_type = field_rev.ty.into();
             let type_option_data = get_type_option_data(&field_rev, &field_type).await?;
-            let data = FieldTypeOptionDataPB {
+            let data = TypeOptionPB {
                 grid_id: params.grid_id,
                 field: field_rev.into(),
                 type_option_data,
@@ -190,7 +190,7 @@ pub(crate) async fn get_field_type_option_data_handler(
 pub(crate) async fn create_field_type_option_data_handler(
     data: Data<CreateFieldPayloadPB>,
     manager: AppData<Arc<GridManager>>,
-) -> DataResult<FieldTypeOptionDataPB, FlowyError> {
+) -> DataResult<TypeOptionPB, FlowyError> {
     let params: CreateFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     let field_rev = editor
@@ -199,7 +199,7 @@ pub(crate) async fn create_field_type_option_data_handler(
     let field_type: FieldType = field_rev.ty.into();
     let type_option_data = get_type_option_data(&field_rev, &field_type).await?;
 
-    data_result(FieldTypeOptionDataPB {
+    data_result(TypeOptionPB {
         grid_id: params.grid_id,
         field: field_rev.into(),
         type_option_data,
@@ -289,13 +289,13 @@ pub(crate) async fn create_table_row_handler(
 
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn get_cell_handler(
-    data: Data<GridCellIdPB>,
+    data: Data<CellPathPB>,
     manager: AppData<Arc<GridManager>>,
-) -> DataResult<GridCellPB, FlowyError> {
-    let params: GridCellIdParams = data.into_inner().try_into()?;
+) -> DataResult<CellPB, FlowyError> {
+    let params: CellPathParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     match editor.get_cell(&params).await {
-        None => data_result(GridCellPB::empty(&params.field_id)),
+        None => data_result(CellPB::empty(&params.field_id)),
         Some(cell) => data_result(cell),
     }
 }
@@ -307,7 +307,7 @@ pub(crate) async fn update_cell_handler(
 ) -> Result<(), FlowyError> {
     let changeset: CellChangesetPB = data.into_inner();
     let editor = manager.get_grid_editor(&changeset.grid_id).await?;
-    let _ = editor.update_cell(changeset).await?;
+    let _ = editor.update_cell_with_changeset(changeset).await?;
     Ok(())
 }
 
@@ -330,7 +330,7 @@ pub(crate) async fn new_select_option_handler(
 
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_select_option_handler(
-    data: Data<SelectOptionChangesetPayloadPB>,
+    data: Data<SelectOptionChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let changeset: SelectOptionChangeset = data.into_inner().try_into()?;
@@ -372,7 +372,7 @@ pub(crate) async fn update_select_option_handler(
                 };
                 let cloned_editor = editor.clone();
                 tokio::spawn(async move {
-                    match cloned_editor.update_cell(changeset).await {
+                    match cloned_editor.update_cell_with_changeset(changeset).await {
                         Ok(_) => {}
                         Err(e) => tracing::error!("{}", e),
                     }
@@ -387,10 +387,10 @@ pub(crate) async fn update_select_option_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_select_option_handler(
-    data: Data<GridCellIdPB>,
+    data: Data<CellPathPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<SelectOptionCellDataPB, FlowyError> {
-    let params: GridCellIdParams = data.into_inner().try_into()?;
+    let params: CellPathParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id).await?;
     match editor.get_field_rev(&params.field_id).await {
         None => {
@@ -416,23 +416,32 @@ pub(crate) async fn get_select_option_handler(
 
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_select_option_cell_handler(
-    data: Data<SelectOptionCellChangesetPayloadPB>,
+    data: Data<SelectOptionCellChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let params: SelectOptionCellChangesetParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.cell_identifier.grid_id).await?;
-    let _ = editor.update_cell(params.into()).await?;
+    let _ = editor.update_cell_with_changeset(params.into()).await?;
     Ok(())
 }
 
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn update_date_cell_handler(
-    data: Data<DateChangesetPayloadPB>,
+    data: Data<DateChangesetPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: DateChangesetParams = data.into_inner().try_into()?;
-    let editor = manager.get_grid_editor(&params.cell_identifier.grid_id).await?;
-    let _ = editor.update_cell(params.into()).await?;
+    let data = data.into_inner();
+    let cell_path: CellPathParams = data.cell_path.try_into()?;
+    let content = DateCellChangeset {
+        date: data.date,
+        time: data.time,
+        is_utc: data.is_utc,
+    };
+
+    let editor = manager.get_grid_editor(&cell_path.grid_id).await?;
+    let _ = editor
+        .update_cell(cell_path.grid_id, cell_path.row_id, cell_path.field_id, content)
+        .await?;
     Ok(())
 }
 

+ 23 - 23
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -74,22 +74,22 @@ pub enum GridEvent {
 
     /// [UpdateGridSetting] event is used to update the grid's settings.
     ///
-    /// The event handler accepts [GridSettingChangesetPayloadPB] and return errors if failed to modify the grid's settings.
-    #[event(input = "GridSettingChangesetPayloadPB")]
+    /// The event handler accepts [GridSettingChangesetPB] and return errors if failed to modify the grid's settings.
+    #[event(input = "GridSettingChangesetPB")]
     UpdateGridSetting = 3,
 
     /// [GetFields] event is used to get the grid's settings.
     ///
-    /// The event handler accepts a [QueryFieldPayloadPB] and returns a [RepeatedFieldPB]
+    /// The event handler accepts a [GetFieldPayloadPB] and returns a [RepeatedFieldPB]
     /// if there are no errors.
-    #[event(input = "QueryFieldPayloadPB", output = "RepeatedFieldPB")]
+    #[event(input = "GetFieldPayloadPB", output = "RepeatedFieldPB")]
     GetFields = 10,
 
     /// [UpdateField] event is used to update a field's attributes.
     ///
-    /// The event handler accepts a [FieldChangesetPayloadPB] and returns errors if failed to modify the
+    /// The event handler accepts a [FieldChangesetPB] and returns errors if failed to modify the
     /// field.
-    #[event(input = "FieldChangesetPayloadPB")]
+    #[event(input = "FieldChangesetPB")]
     UpdateField = 11,
 
     /// [UpdateFieldTypeOption] event is used to update the field's type-option data. Certain field
@@ -100,9 +100,9 @@ pub enum GridEvent {
     /// Check out [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid#fieldtype)
     /// for more information.
     ///
-    /// The event handler accepts a [UpdateFieldTypeOptionPayloadPB] and returns errors if failed to modify the
+    /// The event handler accepts a [TypeOptionChangesetPB] and returns errors if failed to modify the
     /// field.
-    #[event(input = "UpdateFieldTypeOptionPayloadPB")]
+    #[event(input = "TypeOptionChangesetPB")]
     UpdateFieldTypeOption = 12,
 
     /// [DeleteField] event is used to delete a Field. [DeleteFieldPayloadPB] is the context that
@@ -113,7 +113,7 @@ pub enum GridEvent {
     /// [SwitchToField] event is used to update the current Field's type.
     /// It will insert a new FieldTypeOptionData if the new FieldType doesn't exist before, otherwise
     /// reuse the existing FieldTypeOptionData. You could check the [GridRevisionPad] for more details.
-    #[event(input = "EditFieldPayloadPB")]
+    #[event(input = "EditFieldChangesetPB")]
     SwitchToField = 20,
 
     /// [DuplicateField] event is used to duplicate a Field. The duplicated field data is kind of
@@ -130,17 +130,17 @@ pub enum GridEvent {
     #[event(input = "MoveFieldPayloadPB")]
     MoveField = 22,
 
-    /// [FieldTypeOptionIdPB] event is used to get the FieldTypeOption data for a specific field type.
+    /// [TypeOptionPathPB] event is used to get the FieldTypeOption data for a specific field type.
     ///
-    /// Check out the [FieldTypeOptionDataPB] for more details. If the [FieldTypeOptionData] does exist
+    /// Check out the [TypeOptionPB] for more details. If the [FieldTypeOptionData] does exist
     /// for the target type, the [TypeOptionBuilder] will create the default data for that type.
     ///
-    /// Return the [FieldTypeOptionDataPB] if there are no errors.
-    #[event(input = "FieldTypeOptionIdPB", output = "FieldTypeOptionDataPB")]
+    /// Return the [TypeOptionPB] if there are no errors.
+    #[event(input = "TypeOptionPathPB", output = "TypeOptionPB")]
     GetFieldTypeOption = 23,
 
     /// [CreateFieldTypeOption] event is used to create a new FieldTypeOptionData.
-    #[event(input = "CreateFieldPayloadPB", output = "FieldTypeOptionDataPB")]
+    #[event(input = "CreateFieldPayloadPB", output = "TypeOptionPB")]
     CreateFieldTypeOption = 24,
 
     /// [NewSelectOption] event is used to create a new select option. Returns a [SelectOptionPB] if
@@ -149,18 +149,18 @@ pub enum GridEvent {
     NewSelectOption = 30,
 
     /// [GetSelectOptionCellData] event is used to get the select option data for cell editing.
-    /// [GridCellIdPB] locate which cell data that will be read from. The return value, [SelectOptionCellDataPB]
+    /// [CellPathPB] locate which cell data that will be read from. The return value, [SelectOptionCellDataPB]
     /// contains the available options and the currently selected options.
-    #[event(input = "GridCellIdPB", output = "SelectOptionCellDataPB")]
+    #[event(input = "CellPathPB", output = "SelectOptionCellDataPB")]
     GetSelectOptionCellData = 31,
 
     /// [UpdateSelectOption] event is used to update a FieldTypeOptionData whose field_type is
     /// FieldType::SingleSelect or FieldType::MultiSelect.
     ///
     /// This event may trigger the GridNotification::DidUpdateCell event.
-    /// For example, GridNotification::DidUpdateCell will be triggered if the [SelectOptionChangesetPayloadPB]
+    /// For example, GridNotification::DidUpdateCell will be triggered if the [SelectOptionChangesetPB]
     /// carries a change that updates the name of the option.
-    #[event(input = "SelectOptionChangesetPayloadPB")]
+    #[event(input = "SelectOptionChangesetPB")]
     UpdateSelectOption = 32,
 
     #[event(input = "CreateTableRowPayloadPB", output = "RowPB")]
@@ -180,7 +180,7 @@ pub enum GridEvent {
     #[event(input = "MoveRowPayloadPB")]
     MoveRow = 54,
 
-    #[event(input = "GridCellIdPB", output = "GridCellPB")]
+    #[event(input = "CellPathPB", output = "CellPB")]
     GetCell = 70,
 
     /// [UpdateCell] event is used to update the cell content. The passed in data, [CellChangesetPB],
@@ -196,16 +196,16 @@ pub enum GridEvent {
     #[event(input = "CellChangesetPB")]
     UpdateCell = 71,
 
-    /// [UpdateSelectOptionCell] event is used to update a select option cell's data. [SelectOptionCellChangesetPayloadPB]
+    /// [UpdateSelectOptionCell] event is used to update a select option cell's data. [SelectOptionCellChangesetPB]
     /// contains options that will be deleted or inserted. It can be cast to [CellChangesetPB] that
     /// will be used by the `update_cell` function.
-    #[event(input = "SelectOptionCellChangesetPayloadPB")]
+    #[event(input = "SelectOptionCellChangesetPB")]
     UpdateSelectOptionCell = 72,
 
-    /// [UpdateDateCell] event is used to update a date cell's data. [DateChangesetPayloadPB]
+    /// [UpdateDateCell] event is used to update a date cell's data. [DateChangesetPB]
     /// contains the date and the time string. It can be cast to [CellChangesetPB] that
     /// will be used by the `update_cell` function.
-    #[event(input = "DateChangesetPayloadPB")]
+    #[event(input = "DateChangesetPB")]
     UpdateDateCell = 80,
 
     #[event(input = "GridIdPB", output = "RepeatedGridGroupPB")]

+ 10 - 9
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -97,7 +97,7 @@ pub trait CellDataOperation<CD, CS> {
     /// For example:
     /// SelectOptionCellChangeset,DateCellChangeset. etc.
     ///  
-    fn apply_changeset(&self, changeset: CellDataChangeset<CS>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
+    fn apply_changeset(&self, changeset: AnyCellChangeset<CS>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
 }
 
 /// changeset: It will be deserialized into specific data base on the FieldType.
@@ -276,9 +276,10 @@ pub fn insert_checkbox_cell(is_check: bool, field_rev: &FieldRevision) -> CellRe
 }
 
 pub fn insert_date_cell(timestamp: i64, field_rev: &FieldRevision) -> CellRevision {
-    let cell_data = serde_json::to_string(&DateCellChangesetPB {
+    let cell_data = serde_json::to_string(&DateCellChangeset {
         date: Some(timestamp.to_string()),
         time: None,
+        is_utc: true,
     })
     .unwrap();
     let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
@@ -356,9 +357,9 @@ pub trait FromCellChangeset {
         Self: Sized;
 }
 
-pub struct CellDataChangeset<T>(pub Option<T>);
+pub struct AnyCellChangeset<T>(pub Option<T>);
 
-impl<T> CellDataChangeset<T> {
+impl<T> AnyCellChangeset<T> {
     pub fn try_into_inner(self) -> FlowyResult<T> {
         match self.0 {
             None => Err(ErrorCode::InvalidData.into()),
@@ -367,22 +368,22 @@ impl<T> CellDataChangeset<T> {
     }
 }
 
-impl<T, C: ToString> std::convert::From<C> for CellDataChangeset<T>
+impl<T, C: ToString> std::convert::From<C> for AnyCellChangeset<T>
 where
     T: FromCellChangeset,
 {
     fn from(changeset: C) -> Self {
         match T::from_changeset(changeset.to_string()) {
-            Ok(data) => CellDataChangeset(Some(data)),
+            Ok(data) => AnyCellChangeset(Some(data)),
             Err(e) => {
                 tracing::error!("Deserialize CellDataChangeset failed: {}", e);
-                CellDataChangeset(None)
+                AnyCellChangeset(None)
             }
         }
     }
 }
-impl std::convert::From<String> for CellDataChangeset<String> {
+impl std::convert::From<String> for AnyCellChangeset<String> {
     fn from(s: String) -> Self {
-        CellDataChangeset(Some(s))
+        AnyCellChangeset(Some(s))
     }
 }

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::{BoxTypeOptionBuilder, CheckboxCellData, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -80,7 +80,7 @@ impl CellDataOperation<CheckboxCellData, String> for CheckboxTypeOptionPB {
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<String>,
+        changeset: AnyCellChangeset<String>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.try_into_inner()?;

+ 86 - 31
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs

@@ -1,30 +1,60 @@
 use crate::entities::{DateFilterCondition, DateFilterPB};
 use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
 use crate::services::field::{DateTimestamp, DateTypeOptionPB};
+use chrono::NaiveDateTime;
 use flowy_error::FlowyResult;
 
 impl DateFilterPB {
-    pub fn is_visible<T: Into<i64>>(&self, cell_timestamp: T) -> bool {
-        if self.start.is_none() {
-            return false;
-        }
-        let cell_timestamp = cell_timestamp.into();
-        let start_timestamp = *self.start.as_ref().unwrap();
-        // We assume that the cell_timestamp doesn't contain hours, just day.
-        match self.condition {
-            DateFilterCondition::DateIs => cell_timestamp == start_timestamp,
-            DateFilterCondition::DateBefore => cell_timestamp < start_timestamp,
-            DateFilterCondition::DateAfter => cell_timestamp > start_timestamp,
-            DateFilterCondition::DateOnOrBefore => cell_timestamp <= start_timestamp,
-            DateFilterCondition::DateOnOrAfter => cell_timestamp >= start_timestamp,
-            DateFilterCondition::DateWithIn => {
-                if let Some(end_timestamp) = self.end.as_ref() {
-                    cell_timestamp >= start_timestamp && cell_timestamp <= *end_timestamp
-                } else {
-                    false
+    pub fn is_visible<T: Into<Option<i64>>>(&self, cell_timestamp: T) -> bool {
+        match cell_timestamp.into() {
+            None => DateFilterCondition::DateIsEmpty == self.condition,
+            Some(timestamp) => {
+                match self.condition {
+                    DateFilterCondition::DateIsNotEmpty => {
+                        return true;
+                    }
+                    DateFilterCondition::DateIsEmpty => {
+                        return false;
+                    }
+                    _ => {}
+                }
+
+                let cell_time = NaiveDateTime::from_timestamp(timestamp, 0);
+                let cell_date = cell_time.date();
+                match self.timestamp {
+                    None => {
+                        if self.start.is_none() {
+                            return true;
+                        }
+
+                        if self.end.is_none() {
+                            return true;
+                        }
+
+                        let start_time = NaiveDateTime::from_timestamp(*self.start.as_ref().unwrap(), 0);
+                        let start_date = start_time.date();
+
+                        let end_time = NaiveDateTime::from_timestamp(*self.end.as_ref().unwrap(), 0);
+                        let end_date = end_time.date();
+
+                        cell_date >= start_date && cell_date <= end_date
+                    }
+                    Some(timestamp) => {
+                        let expected_timestamp = NaiveDateTime::from_timestamp(timestamp, 0);
+                        let expected_date = expected_timestamp.date();
+
+                        // We assume that the cell_timestamp doesn't contain hours, just day.
+                        match self.condition {
+                            DateFilterCondition::DateIs => cell_date == expected_date,
+                            DateFilterCondition::DateBefore => cell_date < expected_date,
+                            DateFilterCondition::DateAfter => cell_date > expected_date,
+                            DateFilterCondition::DateOnOrBefore => cell_date <= expected_date,
+                            DateFilterCondition::DateOnOrAfter => cell_date >= expected_date,
+                            _ => true,
+                        }
+                    }
                 }
             }
-            DateFilterCondition::DateIsEmpty => cell_timestamp == 0_i64,
         }
     }
 }
@@ -49,11 +79,12 @@ mod tests {
     fn date_filter_is_test() {
         let filter = DateFilterPB {
             condition: DateFilterCondition::DateIs,
-            start: Some(123),
+            timestamp: Some(1668387885),
             end: None,
+            start: None,
         };
 
-        for (val, visible) in vec![(123, true), (12, false)] {
+        for (val, visible) in vec![(1668387885, true), (1647251762, false)] {
             assert_eq!(filter.is_visible(val as i64), visible);
         }
     }
@@ -61,23 +92,26 @@ mod tests {
     fn date_filter_before_test() {
         let filter = DateFilterPB {
             condition: DateFilterCondition::DateBefore,
-            start: Some(123),
+            timestamp: Some(1668387885),
+            start: None,
             end: None,
         };
 
-        for (val, visible) in vec![(123, false), (122, true)] {
-            assert_eq!(filter.is_visible(val as i64), visible);
+        for (val, visible, msg) in vec![(1668387884, false, "1"), (1647251762, true, "2")] {
+            assert_eq!(filter.is_visible(val as i64), visible, "{}", msg);
         }
     }
+
     #[test]
     fn date_filter_before_or_on_test() {
         let filter = DateFilterPB {
             condition: DateFilterCondition::DateOnOrBefore,
-            start: Some(123),
+            timestamp: Some(1668387885),
+            start: None,
             end: None,
         };
 
-        for (val, visible) in vec![(123, true), (122, true)] {
+        for (val, visible) in vec![(1668387884, true), (1668387885, true)] {
             assert_eq!(filter.is_visible(val as i64), visible);
         }
     }
@@ -85,24 +119,45 @@ mod tests {
     fn date_filter_after_test() {
         let filter = DateFilterPB {
             condition: DateFilterCondition::DateAfter,
-            start: Some(123),
+            timestamp: Some(1668387885),
+            start: None,
             end: None,
         };
 
-        for (val, visible) in vec![(1234, true), (122, false), (0, false)] {
+        for (val, visible) in vec![(1668387888, false), (1668531885, true), (0, false)] {
             assert_eq!(filter.is_visible(val as i64), visible);
         }
     }
+
     #[test]
     fn date_filter_within_test() {
         let filter = DateFilterPB {
             condition: DateFilterCondition::DateWithIn,
-            start: Some(123),
-            end: Some(130),
+            start: Some(1668272685), // 11/13
+            end: Some(1668618285),   // 11/17
+            timestamp: None,
         };
 
-        for (val, visible) in vec![(123, true), (130, true), (132, false)] {
+        for (val, visible, _msg) in vec![
+            (1668272685, true, "11/13"),
+            (1668359085, true, "11/14"),
+            (1668704685, false, "11/18"),
+        ] {
             assert_eq!(filter.is_visible(val as i64), visible);
         }
     }
+
+    #[test]
+    fn date_filter_is_empty_test() {
+        let filter = DateFilterPB {
+            condition: DateFilterCondition::DateIsEmpty,
+            start: None,
+            end: None,
+            timestamp: None,
+        };
+
+        for (val, visible) in vec![(None, true), (Some(123), false)] {
+            assert_eq!(filter.is_visible(val), visible);
+        }
+    }
 }

+ 28 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs

@@ -4,6 +4,8 @@ mod tests {
     use crate::services::cell::CellDataOperation;
     use crate::services::field::*;
     // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOptionPB, TimeFormat};
+    use chrono::format::strftime::StrftimeItems;
+    use chrono::{FixedOffset, NaiveDateTime};
     use grid_rev_model::FieldRevision;
     use strum::IntoEnumIterator;
 
@@ -113,6 +115,30 @@ mod tests {
             &field_rev,
         );
     }
+
+    #[test]
+    fn utc_to_native_test() {
+        let native_timestamp = 1647251762;
+        let native = NaiveDateTime::from_timestamp(native_timestamp, 0);
+
+        let utc = chrono::DateTime::<chrono::Utc>::from_utc(native, chrono::Utc);
+        // utc_timestamp doesn't  carry timezone
+        let utc_timestamp = utc.timestamp();
+        assert_eq!(native_timestamp, utc_timestamp);
+
+        let format = "%m/%d/%Y %I:%M %p".to_string();
+        let native_time_str = format!("{}", native.format_with_items(StrftimeItems::new(&format)));
+        let utc_time_str = format!("{}", utc.format_with_items(StrftimeItems::new(&format)));
+        assert_eq!(native_time_str, utc_time_str);
+
+        // Mon Mar 14 2022 17:56:02 GMT+0800 (China Standard Time)
+        let gmt_8_offset = FixedOffset::east(8 * 3600);
+        let china_local = chrono::DateTime::<chrono::Local>::from_utc(native, gmt_8_offset);
+        let china_local_time = format!("{}", china_local.format_with_items(StrftimeItems::new(&format)));
+
+        assert_eq!(china_local_time, "03/14/2022 05:56 PM");
+    }
+
     fn assert_date<T: ToString>(
         type_option: &DateTypeOptionPB,
         timestamp: T,
@@ -120,9 +146,10 @@ mod tests {
         expected_str: &str,
         field_rev: &FieldRevision,
     ) {
-        let s = serde_json::to_string(&DateCellChangesetPB {
+        let s = serde_json::to_string(&DateCellChangeset {
             date: Some(timestamp.to_string()),
             time: include_time_str,
+            is_utc: false,
         })
         .unwrap();
         let encoded_data = type_option.apply_changeset(s.into(), None).unwrap();

+ 9 - 16
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs

@@ -1,8 +1,8 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::{
-    BoxTypeOptionBuilder, DateCellChangesetPB, DateCellDataPB, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
+    BoxTypeOptionBuilder, DateCellChangeset, DateCellDataPB, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
 };
 use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
@@ -32,13 +32,9 @@ impl DateTypeOptionPB {
         Self::default()
     }
 
-    fn today_desc_from_timestamp<T: AsRef<i64>>(&self, timestamp: T) -> DateCellDataPB {
-        let timestamp = *timestamp.as_ref();
+    fn today_desc_from_timestamp<T: Into<i64>>(&self, timestamp: T) -> DateCellDataPB {
+        let timestamp = timestamp.into();
         let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
-        self.date_from_native(native)
-    }
-
-    fn date_from_native(&self, native: chrono::NaiveDateTime) -> DateCellDataPB {
         if native.timestamp() == 0 {
             return DateCellDataPB::default();
         }
@@ -106,11 +102,6 @@ impl DateTypeOptionPB {
         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)
     }
@@ -140,7 +131,7 @@ impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
     }
 }
 
-impl CellDataOperation<DateTimestamp, DateCellChangesetPB> for DateTypeOptionPB {
+impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
     fn decode_cell_data(
         &self,
         cell_data: CellData<DateTimestamp>,
@@ -159,7 +150,7 @@ impl CellDataOperation<DateTimestamp, DateCellChangesetPB> for DateTypeOptionPB
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<DateCellChangesetPB>,
+        changeset: AnyCellChangeset<DateCellChangeset>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.try_into_inner()?;
@@ -168,7 +159,9 @@ impl CellDataOperation<DateTimestamp, DateCellChangesetPB> for DateTypeOptionPB
             Some(date_timestamp) => match (self.include_time, changeset.time) {
                 (true, Some(time)) => {
                     let time = Some(time.trim().to_uppercase());
-                    let utc = self.utc_date_time_from_timestamp(date_timestamp);
+                    let native = NaiveDateTime::from_timestamp(date_timestamp, 0);
+
+                    let utc = self.utc_date_time_from_native(native);
                     self.timestamp_from_utc_with_time(&utc, &time)?
                 }
                 _ => date_timestamp,

+ 24 - 50
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs

@@ -1,11 +1,8 @@
-use crate::entities::CellChangesetPB;
-use crate::entities::{GridCellIdPB, GridCellIdParams};
+use crate::entities::CellPathPB;
 use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellChangeset, FromCellString};
 use bytes::Bytes;
-
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_error::{internal_error, ErrorCode, FlowyResult};
-
+use flowy_error::{internal_error, FlowyResult};
 use serde::{Deserialize, Serialize};
 use strum_macros::EnumIter;
 
@@ -22,59 +19,28 @@ pub struct DateCellDataPB {
 }
 
 #[derive(Clone, Debug, Default, ProtoBuf)]
-pub struct DateChangesetPayloadPB {
+pub struct DateChangesetPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdPB,
+    pub cell_path: CellPathPB,
 
     #[pb(index = 2, one_of)]
     pub date: Option<String>,
 
     #[pb(index = 3, one_of)]
     pub time: Option<String>,
-}
-
-pub struct DateChangesetParams {
-    pub cell_identifier: GridCellIdParams,
-    pub date: Option<String>,
-    pub time: Option<String>,
-}
-
-impl TryInto<DateChangesetParams> for DateChangesetPayloadPB {
-    type Error = ErrorCode;
 
-    fn try_into(self) -> Result<DateChangesetParams, Self::Error> {
-        let cell_identifier: GridCellIdParams = self.cell_identifier.try_into()?;
-        Ok(DateChangesetParams {
-            cell_identifier,
-            date: self.date,
-            time: self.time,
-        })
-    }
-}
-
-impl std::convert::From<DateChangesetParams> for CellChangesetPB {
-    fn from(params: DateChangesetParams) -> Self {
-        let changeset = DateCellChangesetPB {
-            date: params.date,
-            time: params.time,
-        };
-        let content = serde_json::to_string(&changeset).unwrap();
-        CellChangesetPB {
-            grid_id: params.cell_identifier.grid_id,
-            row_id: params.cell_identifier.row_id,
-            field_id: params.cell_identifier.field_id,
-            content,
-        }
-    }
+    #[pb(index = 4)]
+    pub is_utc: bool,
 }
 
 #[derive(Clone, Serialize, Deserialize)]
-pub struct DateCellChangesetPB {
+pub struct DateCellChangeset {
     pub date: Option<String>,
     pub time: Option<String>,
+    pub is_utc: bool,
 }
 
-impl DateCellChangesetPB {
+impl DateCellChangeset {
     pub fn date_timestamp(&self) -> Option<i64> {
         if let Some(date) = &self.date {
             match date.parse::<i64>() {
@@ -87,22 +53,30 @@ impl DateCellChangesetPB {
     }
 }
 
-impl FromCellChangeset for DateCellChangesetPB {
+impl FromCellChangeset for DateCellChangeset {
     fn from_changeset(changeset: String) -> FlowyResult<Self>
     where
         Self: Sized,
     {
-        serde_json::from_str::<DateCellChangesetPB>(&changeset).map_err(internal_error)
+        serde_json::from_str::<DateCellChangeset>(&changeset).map_err(internal_error)
     }
 }
-pub struct DateTimestamp(i64);
-impl AsRef<i64> for DateTimestamp {
-    fn as_ref(&self) -> &i64 {
-        &self.0
+
+impl ToString for DateCellChangeset {
+    fn to_string(&self) -> String {
+        serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
     }
 }
 
+pub struct DateTimestamp(Option<i64>);
+
 impl std::convert::From<DateTimestamp> for i64 {
+    fn from(timestamp: DateTimestamp) -> Self {
+        timestamp.0.unwrap_or(0)
+    }
+}
+
+impl std::convert::From<DateTimestamp> for Option<i64> {
     fn from(timestamp: DateTimestamp) -> Self {
         timestamp.0
     }
@@ -113,7 +87,7 @@ impl FromCellString for DateTimestamp {
     where
         Self: Sized,
     {
-        let num = s.parse::<i64>().unwrap_or(0);
+        let num = s.parse::<i64>().ok();
         Ok(DateTimestamp(num))
     }
 }

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::type_options::number_type_option::format::*;
 use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
 use bytes::Bytes;
@@ -146,7 +146,7 @@ impl CellDataOperation<String, String> for NumberTypeOptionPB {
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<String>,
+        changeset: AnyCellChangeset<String>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.try_into_inner()?;

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
@@ -50,7 +50,7 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSele
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<SelectOptionCellChangeset>,
+        changeset: AnyCellChangeset<SelectOptionCellChangeset>,
         cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let content_changeset = changeset.try_into_inner()?;

+ 66 - 21
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs

@@ -12,26 +12,27 @@ impl SelectOptionFilterPB {
         match self.condition {
             SelectOptionCondition::OptionIs => {
                 if self.option_ids.len() != selected_option_ids.len() {
-                    return true;
+                    return false;
                 }
-
                 // if selected options equal to filter's options, then the required_options will be empty.
                 let required_options = self
                     .option_ids
                     .iter()
-                    .filter(|id| !selected_option_ids.contains(id))
+                    .filter(|id| selected_option_ids.contains(id))
                     .collect::<Vec<_>>();
-
-                // https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect
-                !required_options.is_empty()
+                required_options.len() == selected_option_ids.len()
             }
             SelectOptionCondition::OptionIsNot => {
-                for option_id in selected_option_ids {
-                    if self.option_ids.contains(option_id) {
-                        return true;
-                    }
+                if self.option_ids.len() != selected_option_ids.len() {
+                    return true;
                 }
-                false
+                let required_options = self
+                    .option_ids
+                    .iter()
+                    .filter(|id| selected_option_ids.contains(id))
+                    .collect::<Vec<_>>();
+
+                required_options.len() != selected_option_ids.len()
             }
             SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
             SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
@@ -67,43 +68,87 @@ mod tests {
     use crate::services::field::selection_type_option::{SelectOptionPB, SelectedSelectOptions};
 
     #[test]
-    fn select_option_filter_is_test() {
+    fn select_option_filter_is_empty_test() {
+        let option = SelectOptionPB::new("A");
+        let filter = SelectOptionFilterPB {
+            condition: SelectOptionCondition::OptionIsEmpty,
+            option_ids: vec![],
+        };
+
+        assert_eq!(filter.is_visible(&SelectedSelectOptions { options: vec![] }), true);
+
+        assert_eq!(
+            filter.is_visible(&SelectedSelectOptions { options: vec![option] }),
+            false,
+        );
+    }
+    #[test]
+    fn select_option_filter_is_not_test() {
         let option_1 = SelectOptionPB::new("A");
         let option_2 = SelectOptionPB::new("B");
         let option_3 = SelectOptionPB::new("C");
 
-        let filter_1 = SelectOptionFilterPB {
-            condition: SelectOptionCondition::OptionIs,
+        let filter = SelectOptionFilterPB {
+            condition: SelectOptionCondition::OptionIsNot,
             option_ids: vec![option_1.id.clone(), option_2.id.clone()],
         };
 
         assert_eq!(
-            filter_1.is_visible(&SelectedSelectOptions {
+            filter.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_2.clone()],
             }),
             false
         );
 
         assert_eq!(
-            filter_1.is_visible(&SelectedSelectOptions {
+            filter.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_2.clone(), option_3.clone()],
             }),
             true
         );
 
         assert_eq!(
-            filter_1.is_visible(&SelectedSelectOptions {
+            filter.is_visible(&SelectedSelectOptions {
                 options: vec![option_1.clone(), option_3.clone()],
             }),
             true
         );
 
-        assert_eq!(filter_1.is_visible(&SelectedSelectOptions { options: vec![] }), true);
+        assert_eq!(filter.is_visible(&SelectedSelectOptions { options: vec![] }), true);
+    }
+
+    #[test]
+    fn select_option_filter_is_test() {
+        let option_1 = SelectOptionPB::new("A");
+        let option_2 = SelectOptionPB::new("B");
+        let option_3 = SelectOptionPB::new("C");
+
+        let filter = SelectOptionFilterPB {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![option_1.id.clone(), option_2.id.clone()],
+        };
+
+        assert_eq!(
+            filter.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_2.clone()],
+            }),
+            true
+        );
+
         assert_eq!(
-            filter_1.is_visible(&SelectedSelectOptions {
-                options: vec![option_1.clone()],
+            filter.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_2.clone(), option_3.clone()],
             }),
-            true,
+            false
         );
+
+        assert_eq!(
+            filter.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_3.clone()],
+            }),
+            false
+        );
+
+        assert_eq!(filter.is_visible(&SelectedSelectOptions { options: vec![] }), false);
     }
 }

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

@@ -1,5 +1,5 @@
 use crate::entities::parser::NotEmptyStr;
-use crate::entities::{CellChangesetPB, FieldType, GridCellIdPB, GridCellIdParams};
+use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
 use crate::services::cell::{
     CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString,
 };
@@ -353,9 +353,9 @@ impl CellBytesParser for SelectOptionCellDataParser {
 }
 
 #[derive(Clone, Debug, Default, ProtoBuf)]
-pub struct SelectOptionCellChangesetPayloadPB {
+pub struct SelectOptionCellChangesetPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdPB,
+    pub cell_identifier: CellPathPB,
 
     #[pb(index = 2)]
     pub insert_option_ids: Vec<String>,
@@ -365,7 +365,7 @@ pub struct SelectOptionCellChangesetPayloadPB {
 }
 
 pub struct SelectOptionCellChangesetParams {
-    pub cell_identifier: GridCellIdParams,
+    pub cell_identifier: CellPathParams,
     pub insert_option_ids: Vec<String>,
     pub delete_option_ids: Vec<String>,
 }
@@ -386,11 +386,11 @@ impl std::convert::From<SelectOptionCellChangesetParams> for CellChangesetPB {
     }
 }
 
-impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPayloadPB {
+impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<SelectOptionCellChangesetParams, Self::Error> {
-        let cell_identifier: GridCellIdParams = self.cell_identifier.try_into()?;
+        let cell_identifier: CellPathParams = self.cell_identifier.try_into()?;
         let insert_option_ids = self
             .insert_option_ids
             .into_iter()
@@ -485,12 +485,12 @@ pub struct SelectOptionCellDataPB {
     pub select_options: Vec<SelectOptionPB>,
 }
 
-/// [SelectOptionChangesetPayloadPB] describes the changes of a FieldTypeOptionData. For the moment,
+/// [SelectOptionChangesetPB] describes the changes of a FieldTypeOptionData. For the moment,
 /// it is used by [MultiSelectTypeOptionPB] and [SingleSelectTypeOptionPB].
 #[derive(Clone, Debug, Default, ProtoBuf)]
-pub struct SelectOptionChangesetPayloadPB {
+pub struct SelectOptionChangesetPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdPB,
+    pub cell_identifier: CellPathPB,
 
     #[pb(index = 2)]
     pub insert_options: Vec<SelectOptionPB>,
@@ -503,13 +503,13 @@ pub struct SelectOptionChangesetPayloadPB {
 }
 
 pub struct SelectOptionChangeset {
-    pub cell_identifier: GridCellIdParams,
+    pub cell_identifier: CellPathParams,
     pub insert_options: Vec<SelectOptionPB>,
     pub update_options: Vec<SelectOptionPB>,
     pub delete_options: Vec<SelectOptionPB>,
 }
 
-impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPayloadPB {
+impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<SelectOptionChangeset, Self::Error> {

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::field::{
@@ -49,7 +49,7 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSel
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<SelectOptionCellChangeset>,
+        changeset: AnyCellChangeset<SelectOptionCellChangeset>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let content_changeset = changeset.try_into_inner()?;

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

@@ -1,7 +1,7 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{
-    decode_cell_data_to_string, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataIsEmpty,
+    decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellData, CellDataIsEmpty,
     CellDataOperation, CellDisplayable, FromCellString,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
@@ -83,7 +83,7 @@ impl CellDataOperation<String, String> for RichTextTypeOptionPB {
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<String>,
+        changeset: AnyCellChangeset<String>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let data = changeset.try_into_inner()?;

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

@@ -1,6 +1,6 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellDataPB};
 use bytes::Bytes;
 use fancy_regex::Regex;
@@ -73,7 +73,7 @@ impl CellDataOperation<URLCellDataPB, String> for URLTypeOptionPB {
 
     fn apply_changeset(
         &self,
-        changeset: CellDataChangeset<String>,
+        changeset: AnyCellChangeset<String>,
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let content = changeset.try_into_inner()?;

+ 28 - 11
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -1,5 +1,5 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
-use crate::entities::GridCellIdParams;
+use crate::entities::CellPathParams;
 use crate::entities::*;
 use crate::manager::GridUser;
 use crate::services::block_manager::GridBlockManager;
@@ -205,7 +205,7 @@ impl GridRevisionEditor {
     pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
         let _ = self.modify(|grid_pad| Ok(grid_pad.delete_field_rev(field_id)?)).await?;
         let field_order = FieldIdPB::from(field_id);
-        let notified_changeset = FieldChangesetPB::delete(&self.grid_id, vec![field_order]);
+        let notified_changeset = GridFieldChangesetPB::delete(&self.grid_id, vec![field_order]);
         let _ = self.notify_did_update_grid(notified_changeset).await?;
         Ok(())
     }
@@ -443,17 +443,17 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    pub async fn get_cell(&self, params: &GridCellIdParams) -> Option<GridCellPB> {
+    pub async fn get_cell(&self, params: &CellPathParams) -> Option<CellPB> {
         let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
-        Some(GridCellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
+        Some(CellPB::new(&params.field_id, field_type, cell_bytes.to_vec()))
     }
 
-    pub async fn get_cell_bytes(&self, params: &GridCellIdParams) -> Option<CellBytes> {
+    pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellBytes> {
         let (_, cell_data) = self.decode_any_cell_data(params).await?;
         Some(cell_data)
     }
 
-    async fn decode_any_cell_data(&self, params: &GridCellIdParams) -> Option<(FieldType, CellBytes)> {
+    async fn decode_any_cell_data(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
         let field_rev = self.get_field_rev(&params.field_id).await?;
         let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
         let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
@@ -472,7 +472,7 @@ impl GridRevisionEditor {
     }
 
     #[tracing::instrument(level = "trace", skip_all, err)]
-    pub async fn update_cell(&self, cell_changeset: CellChangesetPB) -> FlowyResult<()> {
+    pub async fn update_cell_with_changeset(&self, cell_changeset: CellChangesetPB) -> FlowyResult<()> {
         let CellChangesetPB {
             grid_id,
             row_id,
@@ -503,6 +503,23 @@ impl GridRevisionEditor {
         }
     }
 
+    #[tracing::instrument(level = "trace", skip_all, err)]
+    pub async fn update_cell<T: ToString>(
+        &self,
+        grid_id: String,
+        row_id: String,
+        field_id: String,
+        content: T,
+    ) -> FlowyResult<()> {
+        self.update_cell_with_changeset(CellChangesetPB {
+            grid_id,
+            row_id,
+            field_id,
+            content: content.to_string(),
+        })
+        .await
+    }
+
     pub async fn get_block_meta_revs(&self) -> FlowyResult<Vec<Arc<GridBlockMetaRevision>>> {
         let block_meta_revs = self.grid_pad.read().await.get_block_meta_revs();
         Ok(block_meta_revs)
@@ -673,7 +690,7 @@ impl GridRevisionEditor {
         if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(&field_id) {
             let delete_field_order = FieldIdPB::from(field_id);
             let insert_field = IndexFieldPB::from_field_rev(field_rev, index);
-            let notified_changeset = FieldChangesetPB {
+            let notified_changeset = GridFieldChangesetPB {
                 grid_id: self.grid_id.clone(),
                 inserted_fields: vec![insert_field],
                 deleted_fields: vec![delete_field_order],
@@ -775,7 +792,7 @@ impl GridRevisionEditor {
     async fn notify_did_insert_grid_field(&self, field_id: &str) -> FlowyResult<()> {
         if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(field_id) {
             let index_field = IndexFieldPB::from_field_rev(field_rev, index);
-            let notified_changeset = FieldChangesetPB::insert(&self.grid_id, vec![index_field]);
+            let notified_changeset = GridFieldChangesetPB::insert(&self.grid_id, vec![index_field]);
             let _ = self.notify_did_update_grid(notified_changeset).await?;
         }
         Ok(())
@@ -791,7 +808,7 @@ impl GridRevisionEditor {
             .map(|(index, field)| (index, field.clone()))
         {
             let updated_field = FieldPB::from(field_rev);
-            let notified_changeset = FieldChangesetPB::update(&self.grid_id, vec![updated_field.clone()]);
+            let notified_changeset = GridFieldChangesetPB::update(&self.grid_id, vec![updated_field.clone()]);
             let _ = self.notify_did_update_grid(notified_changeset).await?;
 
             send_dart_notification(field_id, GridNotification::DidUpdateField)
@@ -802,7 +819,7 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    async fn notify_did_update_grid(&self, changeset: FieldChangesetPB) -> FlowyResult<()> {
+    async fn notify_did_update_grid(&self, changeset: GridFieldChangesetPB) -> FlowyResult<()> {
         send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridField)
             .payload(changeset)
             .send();

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -237,8 +237,8 @@ impl GridViewRevisionEditor {
         Ok(())
     }
 
-    pub(crate) async fn is_grouped(&self) -> bool {
-        self.group_controller.read().await.groups().len() > 1
+    pub(crate) async fn group_id(&self) -> String {
+        self.group_controller.read().await.field_id().to_string()
     }
 
     pub(crate) async fn get_view_setting(&self) -> GridSettingPB {

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs

@@ -184,7 +184,7 @@ impl GridViewManager {
     #[tracing::instrument(level = "trace", skip(self), err)]
     pub(crate) async fn did_update_view_field_type_option(&self, field_id: &str) -> FlowyResult<()> {
         let view_editor = self.get_default_view_editor().await?;
-        if view_editor.is_grouped().await {
+        if view_editor.group_id().await == field_id {
             let _ = view_editor.group_by_view_field(field_id).await?;
         }
 

+ 3 - 3
frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs

@@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
 use crate::grid::block_test::util::GridRowTestBuilder;
 use crate::grid::grid_editor::GridEditorTest;
 
-use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, GridLayout, RowPB};
+use flowy_grid::entities::{CellPathParams, CreateRowParams, FieldType, GridLayout, RowPB};
 use flowy_grid::services::field::*;
 use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision};
 use std::collections::HashMap;
@@ -113,7 +113,7 @@ impl GridRowTest {
                 field_type,
                 expected,
             } => {
-                let id = GridCellIdParams {
+                let id = CellPathParams {
                     grid_id: self.grid_id.clone(),
                     field_id,
                     row_id,
@@ -158,7 +158,7 @@ impl GridRowTest {
         }
     }
 
-    async fn compare_cell_content(&self, cell_id: GridCellIdParams, field_type: FieldType, expected: String) {
+    async fn compare_cell_content(&self, cell_id: CellPathParams, field_type: FieldType, expected: String) {
         match field_type {
             FieldType::RichText => {
                 let cell_data = self

+ 3 - 2
frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs

@@ -2,7 +2,7 @@ use flowy_grid::entities::FieldType;
 use std::sync::Arc;
 
 use flowy_grid::services::field::{
-    DateCellChangesetPB, MultiSelectTypeOptionPB, SelectOptionPB, SingleSelectTypeOptionPB,
+    DateCellChangeset, MultiSelectTypeOptionPB, SelectOptionPB, SingleSelectTypeOptionPB,
 };
 use flowy_grid::services::row::RowRevisionBuilder;
 use grid_rev_model::{FieldRevision, RowRevision};
@@ -38,9 +38,10 @@ impl<'a> GridRowTestBuilder<'a> {
     }
 
     pub fn insert_date_cell(&mut self, data: &str) -> String {
-        let value = serde_json::to_string(&DateCellChangesetPB {
+        let value = serde_json::to_string(&DateCellChangeset {
             date: Some(data.to_string()),
             time: None,
+            is_utc: true,
         })
         .unwrap();
         let date_field = self.field_rev_with_type(&FieldType::DateTime);

+ 1 - 1
frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs

@@ -29,7 +29,7 @@ impl GridCellTest {
 
         match script {
             CellScript::UpdateCell { changeset, is_err } => {
-                let result = self.editor.update_cell(changeset).await;
+                let result = self.editor.update_cell_with_changeset(changeset).await;
                 if is_err {
                     assert!(result.is_err())
                 } else {

+ 2 - 1
frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs

@@ -55,9 +55,10 @@ pub fn create_single_select_field(grid_id: &str) -> (CreateFieldParams, FieldRev
 //  The grid will contains all existing field types and there are three empty rows in this grid.
 
 pub fn make_date_cell_string(s: &str) -> String {
-    serde_json::to_string(&DateCellChangesetPB {
+    serde_json::to_string(&DateCellChangeset {
         date: Some(s.to_string()),
         time: None,
+        is_utc: true,
     })
     .unwrap()
 }

+ 64 - 3
frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs

@@ -3,15 +3,76 @@ use crate::grid::filter_test::script::GridFilterTest;
 use flowy_grid::entities::DateFilterCondition;
 
 #[tokio::test]
-#[should_panic]
-async fn grid_filter_date_is_check_test() {
+async fn grid_filter_date_is_test() {
     let mut test = GridFilterTest::new().await;
     let scripts = vec![
         CreateDateFilter {
             condition: DateFilterCondition::DateIs,
-            content: "1647251762".to_string(),
+            start: None,
+            end: None,
+            timestamp: Some(1647251762),
+        },
+        AssertNumberOfRows { expected: 3 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_date_after_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateDateFilter {
+            condition: DateFilterCondition::DateAfter,
+            start: None,
+            end: None,
+            timestamp: Some(1647251762),
+        },
+        AssertNumberOfRows { expected: 2 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_date_on_or_after_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateDateFilter {
+            condition: DateFilterCondition::DateOnOrAfter,
+            start: None,
+            end: None,
+            timestamp: Some(1668359085),
         },
         AssertNumberOfRows { expected: 2 },
     ];
     test.run_scripts(scripts).await;
 }
+
+#[tokio::test]
+async fn grid_filter_date_on_or_before_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateDateFilter {
+            condition: DateFilterCondition::DateOnOrBefore,
+            start: None,
+            end: None,
+            timestamp: Some(1668359085),
+        },
+        AssertNumberOfRows { expected: 4 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_date_within_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateDateFilter {
+            condition: DateFilterCondition::DateWithIn,
+            start: Some(1647251762),
+            end: Some(1668704685),
+            timestamp: None,
+        },
+        AssertNumberOfRows { expected: 5 },
+    ];
+    test.run_scripts(scripts).await;
+}

+ 11 - 4
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -4,7 +4,7 @@
 #![allow(unused_imports)]
 
 use futures::TryFutureExt;
-use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition};
+use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent};
 use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use grid_rev_model::{FieldRevision, FieldTypeRevision};
 use flowy_grid::services::filter::FilterType;
@@ -27,7 +27,9 @@ pub enum FilterScript {
     },
     CreateDateFilter{
         condition: DateFilterCondition,
-        content: String,
+        start: Option<i64>,
+        end: Option<i64>,
+        timestamp: Option<i64>,
     },
     AssertFilterCount {
         count: i32,
@@ -91,10 +93,16 @@ impl GridFilterTest {
                     CreateFilterPayloadPB::new(field_rev, condition, "".to_string());
                 self.insert_filter(payload).await;
             }
-            FilterScript::CreateDateFilter { condition, content} => {
+            FilterScript::CreateDateFilter { condition, start, end, timestamp} => {
                 let field_rev = self.get_field_rev(FieldType::DateTime);
+                let content = DateFilterContent {
+                    start,
+                    end,
+                    timestamp,
+                }.to_string();
                 let payload =
                     CreateFilterPayloadPB::new(field_rev, condition, content);
+
                 self.insert_filter(payload).await;
             }
             FilterScript::AssertFilterCount { count } => {
@@ -125,7 +133,6 @@ impl GridFilterTest {
     }
 
     async fn insert_filter(&self, payload: CreateFilterPayloadPB) {
-
         let params: CreateFilterParams = payload.try_into().unwrap();
         let _ = self.editor.create_filter(params).await.unwrap();
     }

+ 2 - 2
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -236,7 +236,7 @@ fn make_test_grid() -> BuildGridContext {
                     match field_type {
                         FieldType::RichText => row_builder.insert_text_cell("DA"),
                         FieldType::Number => row_builder.insert_number_cell("4"),
-                        FieldType::DateTime => row_builder.insert_date_cell("1647251762"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1668704685"),
                         FieldType::SingleSelect => {
                             row_builder.insert_single_select_cell(|mut options| options.remove(1))
                         }
@@ -250,7 +250,7 @@ fn make_test_grid() -> BuildGridContext {
                     match field_type {
                         FieldType::RichText => row_builder.insert_text_cell("AE"),
                         FieldType::Number => row_builder.insert_number_cell(""),
-                        FieldType::DateTime => row_builder.insert_date_cell("1647251762"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1668359085"),
                         FieldType::SingleSelect => {
                             row_builder.insert_single_select_cell(|mut options| options.remove(2))
                         }