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

Feat/filter date
This commit is contained in:
Nathan.fooo 2022-11-14 17:38:43 +08:00 committed by GitHub
commit c3a41ba9ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 527 additions and 387 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -97,7 +97,7 @@ pub trait CellDataOperation<CD, CS> {
/// For example: /// For example:
/// SelectOptionCellChangeset,DateCellChangeset. etc. /// 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. /// 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 { 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()), date: Some(timestamp.to_string()),
time: None, time: None,
is_utc: true,
}) })
.unwrap(); .unwrap();
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap(); let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
@ -356,9 +357,9 @@ pub trait FromCellChangeset {
Self: Sized; 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> { pub fn try_into_inner(self) -> FlowyResult<T> {
match self.0 { match self.0 {
None => Err(ErrorCode::InvalidData.into()), 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 where
T: FromCellChangeset, T: FromCellChangeset,
{ {
fn from(changeset: C) -> Self { fn from(changeset: C) -> Self {
match T::from_changeset(changeset.to_string()) { match T::from_changeset(changeset.to_string()) {
Ok(data) => CellDataChangeset(Some(data)), Ok(data) => AnyCellChangeset(Some(data)),
Err(e) => { Err(e) => {
tracing::error!("Deserialize CellDataChangeset failed: {}", 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 { fn from(s: String) -> Self {
CellDataChangeset(Some(s)) AnyCellChangeset(Some(s))
} }
} }

View File

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

View File

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

View File

@ -4,6 +4,8 @@ mod tests {
use crate::services::cell::CellDataOperation; use crate::services::cell::CellDataOperation;
use crate::services::field::*; use crate::services::field::*;
// use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOptionPB, TimeFormat}; // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOptionPB, TimeFormat};
use chrono::format::strftime::StrftimeItems;
use chrono::{FixedOffset, NaiveDateTime};
use grid_rev_model::FieldRevision; use grid_rev_model::FieldRevision;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@ -113,6 +115,30 @@ mod tests {
&field_rev, &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>( fn assert_date<T: ToString>(
type_option: &DateTypeOptionPB, type_option: &DateTypeOptionPB,
timestamp: T, timestamp: T,
@ -120,9 +146,10 @@ mod tests {
expected_str: &str, expected_str: &str,
field_rev: &FieldRevision, field_rev: &FieldRevision,
) { ) {
let s = serde_json::to_string(&DateCellChangesetPB { let s = serde_json::to_string(&DateCellChangeset {
date: Some(timestamp.to_string()), date: Some(timestamp.to_string()),
time: include_time_str, time: include_time_str,
is_utc: false,
}) })
.unwrap(); .unwrap();
let encoded_data = type_option.apply_changeset(s.into(), None).unwrap(); let encoded_data = type_option.apply_changeset(s.into(), None).unwrap();

View File

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

View File

@ -1,11 +1,8 @@
use crate::entities::CellChangesetPB; use crate::entities::CellPathPB;
use crate::entities::{GridCellIdPB, GridCellIdParams};
use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellChangeset, FromCellString}; use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellChangeset, FromCellString};
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{internal_error, ErrorCode, FlowyResult}; use flowy_error::{internal_error, FlowyResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum_macros::EnumIter; use strum_macros::EnumIter;
@ -22,59 +19,28 @@ pub struct DateCellDataPB {
} }
#[derive(Clone, Debug, Default, ProtoBuf)] #[derive(Clone, Debug, Default, ProtoBuf)]
pub struct DateChangesetPayloadPB { pub struct DateChangesetPB {
#[pb(index = 1)] #[pb(index = 1)]
pub cell_identifier: GridCellIdPB, pub cell_path: CellPathPB,
#[pb(index = 2, one_of)] #[pb(index = 2, one_of)]
pub date: Option<String>, pub date: Option<String>,
#[pb(index = 3, one_of)] #[pb(index = 3, one_of)]
pub time: Option<String>, pub time: Option<String>,
}
pub struct DateChangesetParams { #[pb(index = 4)]
pub cell_identifier: GridCellIdParams, pub is_utc: bool,
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,
}
}
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct DateCellChangesetPB { pub struct DateCellChangeset {
pub date: Option<String>, pub date: Option<String>,
pub time: Option<String>, pub time: Option<String>,
pub is_utc: bool,
} }
impl DateCellChangesetPB { impl DateCellChangeset {
pub fn date_timestamp(&self) -> Option<i64> { pub fn date_timestamp(&self) -> Option<i64> {
if let Some(date) = &self.date { if let Some(date) = &self.date {
match date.parse::<i64>() { 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> fn from_changeset(changeset: String) -> FlowyResult<Self>
where where
Self: Sized, 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 { 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 { fn from(timestamp: DateTimestamp) -> Self {
timestamp.0 timestamp.0
} }
@ -113,7 +87,7 @@ impl FromCellString for DateTimestamp {
where where
Self: Sized, Self: Sized,
{ {
let num = s.parse::<i64>().unwrap_or(0); let num = s.parse::<i64>().ok();
Ok(DateTimestamp(num)) Ok(DateTimestamp(num))
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::entities::GridCellIdParams; use crate::entities::CellPathParams;
use crate::entities::*; use crate::entities::*;
use crate::manager::GridUser; use crate::manager::GridUser;
use crate::services::block_manager::GridBlockManager; use crate::services::block_manager::GridBlockManager;
@ -205,7 +205,7 @@ impl GridRevisionEditor {
pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> { 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 _ = self.modify(|grid_pad| Ok(grid_pad.delete_field_rev(field_id)?)).await?;
let field_order = FieldIdPB::from(field_id); 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?; let _ = self.notify_did_update_grid(notified_changeset).await?;
Ok(()) Ok(())
} }
@ -443,17 +443,17 @@ impl GridRevisionEditor {
Ok(()) 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?; 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?; let (_, cell_data) = self.decode_any_cell_data(params).await?;
Some(cell_data) 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 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 row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
let cell_rev = row_rev.cells.get(&params.field_id)?.clone(); let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
@ -472,7 +472,7 @@ impl GridRevisionEditor {
} }
#[tracing::instrument(level = "trace", skip_all, err)] #[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 { let CellChangesetPB {
grid_id, grid_id,
row_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>>> { 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(); let block_meta_revs = self.grid_pad.read().await.get_block_meta_revs();
Ok(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) { 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 delete_field_order = FieldIdPB::from(field_id);
let insert_field = IndexFieldPB::from_field_rev(field_rev, index); let insert_field = IndexFieldPB::from_field_rev(field_rev, index);
let notified_changeset = FieldChangesetPB { let notified_changeset = GridFieldChangesetPB {
grid_id: self.grid_id.clone(), grid_id: self.grid_id.clone(),
inserted_fields: vec![insert_field], inserted_fields: vec![insert_field],
deleted_fields: vec![delete_field_order], deleted_fields: vec![delete_field_order],
@ -775,7 +792,7 @@ impl GridRevisionEditor {
async fn notify_did_insert_grid_field(&self, field_id: &str) -> FlowyResult<()> { 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) { 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 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?; let _ = self.notify_did_update_grid(notified_changeset).await?;
} }
Ok(()) Ok(())
@ -791,7 +808,7 @@ impl GridRevisionEditor {
.map(|(index, field)| (index, field.clone())) .map(|(index, field)| (index, field.clone()))
{ {
let updated_field = FieldPB::from(field_rev); 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?; let _ = self.notify_did_update_grid(notified_changeset).await?;
send_dart_notification(field_id, GridNotification::DidUpdateField) send_dart_notification(field_id, GridNotification::DidUpdateField)
@ -802,7 +819,7 @@ impl GridRevisionEditor {
Ok(()) 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) send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridField)
.payload(changeset) .payload(changeset)
.send(); .send();

View File

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

View File

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

View File

@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest; 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 flowy_grid::services::field::*;
use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision}; use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision};
use std::collections::HashMap; use std::collections::HashMap;
@ -113,7 +113,7 @@ impl GridRowTest {
field_type, field_type,
expected, expected,
} => { } => {
let id = GridCellIdParams { let id = CellPathParams {
grid_id: self.grid_id.clone(), grid_id: self.grid_id.clone(),
field_id, field_id,
row_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 { match field_type {
FieldType::RichText => { FieldType::RichText => {
let cell_data = self let cell_data = self

View File

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

View File

@ -29,7 +29,7 @@ impl GridCellTest {
match script { match script {
CellScript::UpdateCell { changeset, is_err } => { 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 { if is_err {
assert!(result.is_err()) assert!(result.is_err())
} else { } else {

View File

@ -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. // 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 { pub fn make_date_cell_string(s: &str) -> String {
serde_json::to_string(&DateCellChangesetPB { serde_json::to_string(&DateCellChangeset {
date: Some(s.to_string()), date: Some(s.to_string()),
time: None, time: None,
is_utc: true,
}) })
.unwrap() .unwrap()
} }

View File

@ -3,15 +3,76 @@ use crate::grid::filter_test::script::GridFilterTest;
use flowy_grid::entities::DateFilterCondition; use flowy_grid::entities::DateFilterCondition;
#[tokio::test] #[tokio::test]
#[should_panic] async fn grid_filter_date_is_test() {
async fn grid_filter_date_is_check_test() {
let mut test = GridFilterTest::new().await; let mut test = GridFilterTest::new().await;
let scripts = vec![ let scripts = vec![
CreateDateFilter { CreateDateFilter {
condition: DateFilterCondition::DateIs, 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 }, AssertNumberOfRows { expected: 2 },
]; ];
test.run_scripts(scripts).await; 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;
}

View File

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

View File

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