From 7950f8170b45d2a450e56b119984916e12509376 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 21 May 2022 21:58:46 +0800 Subject: [PATCH 1/7] chore: optimaze error code --- .../application/grid/cell/date_cal_bloc.dart | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart index a0ba35f9d6..2e70ecddb2 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart @@ -1,6 +1,7 @@ import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -91,7 +92,7 @@ class DateCalBloc extends Bloc { case ErrorCode.InvalidDateTimeFormat: emit(state.copyWith( dateData: Some(newDateData), - timeFormatError: Some(err.toString()), + timeFormatError: Some(messageFromFlowyError(err)), )); break; default: @@ -101,6 +102,18 @@ class DateCalBloc extends Bloc { ); } + String messageFromFlowyError(FlowyError error) { + switch (ErrorCode.valueOf(error.code)!) { + case ErrorCode.EmailFormatInvalid: + return state.copyWith(isSubmitting: false, emailError: some(error.msg), passwordError: none()); + case ErrorCode.PasswordFormatInvalid: + return state.copyWith(isSubmitting: false, passwordError: some(error.msg), emailError: none()); + default: + return state.copyWith(isSubmitting: false, successOrFail: some(right(error))); + } + return ""; + } + @override Future close() async { if (_onCellChangedFn != null) { From 36abd969ac0080483d1a015ec7c66d31e94507e5 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 22 May 2022 13:08:23 +0800 Subject: [PATCH 2/7] chore: format error message of date cell --- .../app_flowy/assets/translations/en.json | 1 + .../application/grid/cell/date_cal_bloc.dart | 23 +++++++++++-------- .../src/widgets/cell/date_cell/date_cell.dart | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 8755f05952..8fc9e90581 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -168,6 +168,7 @@ "dateFormatLocal": "Month/Month/Day", "dateFormatUS": "Month/Month/Day", "timeFormat": " Time format", + "invalidTimeFormat": "Invalid format", "timeFormatTwelveHour": "12 hour", "timeFormatTwentyFourHour": "24 hour", "addSelectOption": "Add an option", diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart index 2e70ecddb2..ff001eaa75 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart @@ -1,4 +1,6 @@ +import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; +import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; @@ -92,7 +94,7 @@ class DateCalBloc extends Bloc { case ErrorCode.InvalidDateTimeFormat: emit(state.copyWith( dateData: Some(newDateData), - timeFormatError: Some(messageFromFlowyError(err)), + timeFormatError: Some(timeFormatPrompt(err)), )); break; default: @@ -102,16 +104,19 @@ class DateCalBloc extends Bloc { ); } - String messageFromFlowyError(FlowyError error) { - switch (ErrorCode.valueOf(error.code)!) { - case ErrorCode.EmailFormatInvalid: - return state.copyWith(isSubmitting: false, emailError: some(error.msg), passwordError: none()); - case ErrorCode.PasswordFormatInvalid: - return state.copyWith(isSubmitting: false, passwordError: some(error.msg), emailError: none()); + String timeFormatPrompt(FlowyError error) { + String msg = LocaleKeys.grid_field_invalidTimeFormat.tr() + ". "; + switch (state.dateTypeOption.timeFormat) { + case TimeFormat.TwelveHour: + msg = msg + "e.g. 01: 00 AM"; + break; + case TimeFormat.TwentyFourHour: + msg = msg + "e.g. 13: 00"; + break; default: - return state.copyWith(isSubmitting: false, successOrFail: some(right(error))); + break; } - return ""; + return msg; } @override diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart index 5f0ccbc834..417c9f270e 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart @@ -18,7 +18,7 @@ abstract class GridCellDelegate { GridCellDelegate get delegate; } -class DateCell extends GridCellWidget { +class DateCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final DateCellStyle? cellStyle; From 1b5b8f19d734e7577994d901bb66f626c0c279a7 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 22 May 2022 13:08:38 +0800 Subject: [PATCH 3/7] chore: auto expand row detail page's cell --- .../grid/src/widgets/cell/cell_builder.dart | 4 +- .../grid/src/widgets/cell/checkbox_cell.dart | 4 +- .../grid/src/widgets/cell/number_cell.dart | 2 +- .../select_option_cell.dart | 6 +-- .../grid/src/widgets/cell/text_cell.dart | 2 +- .../grid/src/widgets/row/row_detail.dart | 45 ++++++++++--------- .../lib/style_widget/hover.dart | 29 ++++++------ .../lib/widget/rounded_input_field.dart | 9 ++-- frontend/rust-lib/flowy-grid/Flowy.toml | 2 +- .../{services => }/entities/cell_entities.rs | 2 +- .../{services => }/entities/field_entities.rs | 0 .../src/{services => }/entities/mod.rs | 0 .../{services => }/entities/row_entities.rs | 0 .../rust-lib/flowy-grid/src/event_handler.rs | 2 +- frontend/rust-lib/flowy-grid/src/lib.rs | 1 + .../field/type_options/date_type_option.rs | 2 +- .../type_options/selection_type_option.rs | 2 +- .../flowy-grid/src/services/grid_editor.rs | 2 +- .../rust-lib/flowy-grid/src/services/mod.rs | 1 - 19 files changed, 61 insertions(+), 54 deletions(-) rename frontend/rust-lib/flowy-grid/src/{services => }/entities/cell_entities.rs (96%) rename frontend/rust-lib/flowy-grid/src/{services => }/entities/field_entities.rs (100%) rename frontend/rust-lib/flowy-grid/src/{services => }/entities/mod.rs (100%) rename frontend/rust-lib/flowy-grid/src/{services => }/entities/row_entities.rs (100%) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart index eab2903857..6f74d23ad0 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart @@ -47,13 +47,11 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget extends HoverWidget { +abstract class GridCellWidget implements FlowyHoverWidget { @override final ValueNotifier onFocus = ValueNotifier(false); final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier(); - - GridCellWidget({Key? key}) : super(key: key); } class GridCellRequestFocusNotifier extends ChangeNotifier { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart index c4da2a223f..b2493d55ed 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart @@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; -class CheckboxCell extends GridCellWidget { +class CheckboxCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; CheckboxCell({ required this.cellContextBuilder, @@ -41,7 +41,7 @@ class _CheckboxCellState extends State { onPressed: () => context.read().add(const CheckboxCellEvent.select()), iconPadding: EdgeInsets.zero, icon: icon, - width: 23, + width: 20, ), ); }, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart index c0b3427e65..dc694f48f9 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart @@ -7,7 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; -class NumberCell extends GridCellWidget { +class NumberCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; NumberCell({ diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart index 12415c816d..c57a1865be 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart @@ -20,7 +20,7 @@ class SelectOptionCellStyle extends GridCellStyle { }); } -class SingleSelectCell extends GridCellWidget { +class SingleSelectCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final SelectOptionCellStyle? cellStyle; @@ -74,7 +74,7 @@ class _SingleSelectCellState extends State { } //---------------------------------------------------------------- -class MultiSelectCell extends GridCellWidget { +class MultiSelectCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final SelectOptionCellStyle? cellStyle; @@ -160,7 +160,7 @@ class _SelectOptionCell extends StatelessWidget { .toList(); child = Align( alignment: Alignment.centerLeft, - child: Wrap(children: tags, spacing: 4, runSpacing: 4), + child: Wrap(children: tags, spacing: 4, runSpacing: 2), ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart index 14ace02d28..1563d41d86 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart @@ -13,7 +13,7 @@ class GridTextCellStyle extends GridCellStyle { }); } -class GridTextCell extends GridCellWidget { +class GridTextCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final GridTextCellStyle? cellStyle; GridTextCell({ diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart index 41cbcb1cc1..0ce2b76e6b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart @@ -71,10 +71,11 @@ class _RowDetailPageState extends State { child: Column( children: [ SizedBox( - height: 40, - child: Row( - children: const [Spacer(), _CloseButton()], - )), + height: 40, + child: Row( + children: const [Spacer(), _CloseButton()], + ), + ), Expanded(child: _PropertyList(cellCache: widget.cellCache)), ], ), @@ -153,24 +154,26 @@ class _RowDetailCell extends StatelessWidget { cellCache, style: _buildCellStyle(theme, gridCell.field.fieldType), ); - return SizedBox( - height: 36, - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 150, - child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), - ), - const HSpace(10), - Expanded( - child: FlowyHover2( - child: cell, - contentPadding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + return ConstrainedBox( + constraints: const BoxConstraints(minHeight: 40), + child: IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 150, + child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), ), - ), - ], + const HSpace(10), + Expanded( + child: FlowyHover2( + child: cell, + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), + ), + ), + ], + ), ), ); } diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart index 4f06a40b9b..bb7144974b 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart @@ -102,14 +102,14 @@ class FlowyHoverContainer extends StatelessWidget { } // -abstract class HoverWidget extends StatefulWidget { - const HoverWidget({Key? key}) : super(key: key); +abstract class FlowyHoverWidget extends Widget { + const FlowyHoverWidget({Key? key}) : super(key: key); - ValueNotifier get onFocus; + ValueNotifier? get onFocus; } class FlowyHover2 extends StatefulWidget { - final Widget child; + final FlowyHoverWidget child; final EdgeInsets contentPadding; const FlowyHover2({ required this.child, @@ -123,24 +123,30 @@ class FlowyHover2 extends StatefulWidget { class _FlowyHover2State extends State { late FlowyHoverState _hoverState; + VoidCallback? _listenerFn; @override void initState() { _hoverState = FlowyHoverState(); - if (widget.child is HoverWidget) { - final hoverWidget = widget.child as HoverWidget; - hoverWidget.onFocus.addListener(() { - _hoverState.onFocus = hoverWidget.onFocus.value; - }); + listener() { + _hoverState.onFocus = widget.child.onFocus?.value ?? false; } + _listenerFn = listener; + widget.child.onFocus?.addListener(listener); + super.initState(); } @override void dispose() { _hoverState.dispose(); + + if (_listenerFn != null) { + widget.child.onFocus?.removeListener(_listenerFn!); + _listenerFn = null; + } super.dispose(); } @@ -179,10 +185,7 @@ class _HoverBackground extends StatelessWidget { builder: (context, state, child) { if (state.onHover || state.onFocus) { return FlowyHoverContainer( - style: HoverStyle( - borderRadius: Corners.s6Border, - hoverColor: theme.shader6, - ), + style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), ); } else { return const SizedBox(); diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart index 712fba3c6c..37eb782a2c 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart @@ -132,9 +132,12 @@ class _RoundedInputFieldState extends State { children.add( Align( alignment: Alignment.centerLeft, - child: Text( - widget.errorText, - style: widget.style, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Text( + widget.errorText, + style: widget.style, + ), ), ), ); diff --git a/frontend/rust-lib/flowy-grid/Flowy.toml b/frontend/rust-lib/flowy-grid/Flowy.toml index 1d0d2baea8..835988d41e 100644 --- a/frontend/rust-lib/flowy-grid/Flowy.toml +++ b/frontend/rust-lib/flowy-grid/Flowy.toml @@ -2,7 +2,7 @@ proto_crates = [ "src/event_map.rs", "src/services/field/type_options", - "src/services/entities", + "src/entities", "src/dart_notification.rs" ] event_files = ["src/event_map.rs"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-grid/src/services/entities/cell_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs similarity index 96% rename from frontend/rust-lib/flowy-grid/src/services/entities/cell_entities.rs rename to frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs index 2f408db992..4d09fe0eda 100644 --- a/frontend/rust-lib/flowy-grid/src/services/entities/cell_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs @@ -1,4 +1,4 @@ -use crate::services::entities::{FieldIdentifier, FieldIdentifierPayload}; +use crate::entities::{FieldIdentifier, FieldIdentifierPayload}; use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; diff --git a/frontend/rust-lib/flowy-grid/src/services/entities/field_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/entities/field_entities.rs rename to frontend/rust-lib/flowy-grid/src/entities/field_entities.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/entities/mod.rs b/frontend/rust-lib/flowy-grid/src/entities/mod.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/entities/mod.rs rename to frontend/rust-lib/flowy-grid/src/entities/mod.rs diff --git a/frontend/rust-lib/flowy-grid/src/services/entities/row_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/row_entities.rs similarity index 100% rename from frontend/rust-lib/flowy-grid/src/services/entities/row_entities.rs rename to frontend/rust-lib/flowy-grid/src/entities/row_entities.rs diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 724d898d31..44123a45d9 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -1,5 +1,5 @@ +use crate::entities::*; use crate::manager::GridManager; -use crate::services::entities::*; use crate::services::field::type_options::*; use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_json_str}; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; diff --git a/frontend/rust-lib/flowy-grid/src/lib.rs b/frontend/rust-lib/flowy-grid/src/lib.rs index a3ac3411e2..4f7605b0b8 100644 --- a/frontend/rust-lib/flowy-grid/src/lib.rs +++ b/frontend/rust-lib/flowy-grid/src/lib.rs @@ -6,6 +6,7 @@ pub mod event_map; pub mod manager; mod dart_notification; +pub mod entities; mod protobuf; pub mod services; pub mod util; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 84cfe0e4bf..b2589d4f83 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -1,5 +1,5 @@ +use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; -use crate::services::entities::{CellIdentifier, CellIdentifierPayload}; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData}; use bytes::Bytes; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs index 30ecfabd9d..2ba96968ce 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs @@ -1,5 +1,5 @@ +use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; -use crate::services::entities::{CellIdentifier, CellIdentifierPayload}; use crate::services::field::type_options::util::get_cell_data; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData}; diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 44c826f31d..c161e191c4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -1,7 +1,7 @@ use crate::dart_notification::{send_dart_notification, GridNotification}; +use crate::entities::CellIdentifier; use crate::manager::GridUser; use crate::services::block_meta_manager::GridBlockMetaEditorManager; -use crate::services::entities::CellIdentifier; use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder}; use crate::services::persistence::block_index::BlockIndexPersistence; use crate::services::row::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/mod.rs b/frontend/rust-lib/flowy-grid/src/services/mod.rs index 036efd6ada..c9a8217bd8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/mod.rs @@ -2,7 +2,6 @@ mod util; pub mod block_meta_editor; mod block_meta_manager; -pub mod entities; pub mod field; pub mod grid_editor; pub mod persistence; From f521c18512190dc845f88d4e14ea7ecc3399ceae Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 22 May 2022 13:26:41 +0800 Subject: [PATCH 4/7] fix: overflow of SelectOptionTagCell --- .../cell/select_option_cell/extension.dart | 19 +++++--- .../select_option_editor.dart | 25 +++++------ .../src/widgets/row/cell/number_cell.dart | 44 ------------------- .../grid/src/widgets/row/row_detail.dart | 2 +- 4 files changed, 26 insertions(+), 64 deletions(-) delete mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/cell/number_cell.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart index 3cbf2331fd..6f8212106a 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart @@ -87,7 +87,7 @@ class SelectOptionTag extends StatelessWidget { Widget build(BuildContext context) { return ChoiceChip( pressElevation: 1, - label: FlowyText.medium(name, fontSize: 12), + label: FlowyText.medium(name, fontSize: 12, overflow: TextOverflow.ellipsis), selectedColor: color, backgroundColor: color, labelPadding: const EdgeInsets.symmetric(horizontal: 6), @@ -130,11 +130,18 @@ class SelectOptionTagCell extends StatelessWidget { child: InkWell( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 3), - child: Row(children: [ - SelectOptionTag.fromSelectOption(context: context, option: option), - const Spacer(), - ...children, - ]), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + fit: FlexFit.loose, + flex: 2, + child: SelectOptionTag.fromSelectOption(context: context, option: option), + ), + const Spacer(), + ...children, + ], + ), ), onTap: () => onSelected(option), ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart index f82d52cfb4..01972eb41a 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart @@ -225,7 +225,18 @@ class _SelectOptionCell extends StatelessWidget { height: GridSize.typeOptionItemHeight, child: Row( children: [ - Expanded(child: _body(theme, context)), + Flexible( + fit: FlexFit.loose, + child: SelectOptionTagCell( + option: option, + onSelected: (option) { + context.read().add(SelectOptionEditorEvent.selectOption(option.id)); + }, + children: [ + if (isSelected) svgWidget("grid/checkmark"), + ], + ), + ), FlowyIconButton( width: 30, onPressed: () => _showEditPannel(context), @@ -237,18 +248,6 @@ class _SelectOptionCell extends StatelessWidget { ); } - Widget _body(AppTheme theme, BuildContext context) { - return SelectOptionTagCell( - option: option, - onSelected: (option) { - context.read().add(SelectOptionEditorEvent.selectOption(option.id)); - }, - children: [ - if (isSelected) svgWidget("grid/checkmark"), - ], - ); - } - void _showEditPannel(BuildContext context) { final pannel = SelectOptionTypeOptionEditor( option: option, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/cell/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/cell/number_cell.dart deleted file mode 100644 index 0f3f7c5f32..0000000000 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/cell/number_cell.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:app_flowy/startup/startup.dart'; -import 'package:app_flowy/workspace/application/grid/prelude.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class NumberCell extends StatefulWidget { - final GridCell cellData; - - const NumberCell({ - required this.cellData, - Key? key, - }) : super(key: key); - - @override - State createState() => _NumberCellState(); -} - -class _NumberCellState extends State { - late NumberCellBloc _cellBloc; - - @override - void initState() { - _cellBloc = getIt(param1: widget.cellData); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider.value( - value: _cellBloc, - child: BlocBuilder( - builder: (context, state) { - return Container(); - }, - ), - ); - } - - @override - Future dispose() async { - _cellBloc.close(); - super.dispose(); - } -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart index 0ce2b76e6b..f2b93e5018 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart @@ -67,7 +67,7 @@ class _RowDetailPageState extends State { return bloc; }, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 40), + padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20), child: Column( children: [ SizedBox( From 1ae0b188b1f7b5ebb0807d9bedf864a77072c3e6 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 22 May 2022 23:33:08 +0800 Subject: [PATCH 5/7] refactor: grid unit test --- .../type_options/checkbox_type_option.rs | 84 ++-- .../field/type_options/date_type_option.rs | 175 ++++---- .../field/type_options/number_type_option.rs | 379 +++++++++--------- .../type_options/selection_type_option.rs | 274 ++++++------- .../field/type_options/text_type_option.rs | 150 +++---- .../src/services/row/cell_data_operation.rs | 91 +++-- 6 files changed, 614 insertions(+), 539 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs index 73c3f5f2d7..a3c084de03 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs @@ -44,15 +44,18 @@ const YES: &str = "Yes"; const NO: &str = "No"; impl CellDataOperation for CheckboxTypeOption { - fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - if !type_option_cell_data.is_checkbox() { - return DecodedCellData::default(); - } - let cell_data = type_option_cell_data.data; - if cell_data == YES || cell_data == NO { - return DecodedCellData::from_content(cell_data); - } + fn decode_cell_data>( + &self, + type_option_cell_data: T, + _field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + if !type_option_cell_data.is_checkbox() { + return DecodedCellData::default(); + } + let cell_data = type_option_cell_data.data; + if cell_data == YES || cell_data == NO { + return DecodedCellData::from_content(cell_data); } DecodedCellData::default() @@ -68,7 +71,7 @@ impl CellDataOperation for CheckboxTypeOption { true => YES, false => NO, }; - Ok(TypeOptionCellData::new(s, self.field_type()).json()) + Ok(s.to_string()) } } @@ -90,30 +93,59 @@ mod tests { use crate::services::field::type_options::checkbox_type_option::{NO, YES}; use crate::services::field::CheckboxTypeOption; use crate::services::field::FieldBuilder; - use crate::services::row::CellDataOperation; + use crate::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation}; + use diesel::types::IsNull::No; use flowy_grid_data_model::entities::FieldType; #[test] fn checkout_box_description_test() { - let type_option = CheckboxTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let field_meta = FieldBuilder::from_field_type(&FieldType::Checkbox).build(); + let data = apply_cell_data_changeset("true", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + YES + ); - let data = type_option.apply_changeset("true", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES); + let data = apply_cell_data_changeset("1", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + YES + ); - let data = type_option.apply_changeset("1", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES); + let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + YES + ); - let data = type_option.apply_changeset("yes", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, YES); + let data = apply_cell_data_changeset("false", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + NO + ); - let data = type_option.apply_changeset("false", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO); + let data = apply_cell_data_changeset("no", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + NO + ); - let data = type_option.apply_changeset("no", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO); - - let data = type_option.apply_changeset("123", None).unwrap(); - assert_eq!(type_option.decode_cell_data(data, &field_meta).content, NO); + let data = apply_cell_data_changeset("12", None, &field_meta).unwrap(); + assert_eq!( + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + NO + ); } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index b2589d4f83..3dcc1d81eb 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -136,22 +136,23 @@ impl DateTypeOption { } impl CellDataOperation for DateTypeOption { - fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - // Return default data if the type_option_cell_data is not FieldType::DateTime. - // It happens when switching from one field to another. - // For example: - // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. - if !type_option_cell_data.is_date() { - return DecodedCellData::default(); - } - return match DateCellDataSerde::from_str(&type_option_cell_data.data) { - Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(&serde_cell_data), - Err(_) => DecodedCellData::default(), - }; + fn decode_cell_data>( + &self, + type_option_cell_data: T, + _field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + // Return default data if the type_option_cell_data is not FieldType::DateTime. + // It happens when switching from one field to another. + // For example: + // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. + if !type_option_cell_data.is_date() { + return DecodedCellData::default(); + } + match DateCellDataSerde::from_str(&type_option_cell_data.data) { + Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(&serde_cell_data), + Err(_) => DecodedCellData::default(), } - - DecodedCellData::default() } fn apply_changeset>( @@ -173,7 +174,7 @@ impl CellDataOperation for DateTypeOption { }, }; - Ok(TypeOptionCellData::new(cell_data.to_string(), self.field_type()).json()) + Ok(cell_data.to_string()) } } @@ -410,17 +411,19 @@ mod tests { use crate::services::field::{ DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, }; - use crate::services::row::{CellDataOperation, TypeOptionCellData}; + use crate::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation, TypeOptionCellData}; use flowy_grid_data_model::entities::FieldType; use strum::IntoEnumIterator; #[test] fn date_description_invalid_input_test() { - let type_option = DateTypeOption::default(); let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + let data = apply_cell_data_changeset("1e", None, &field_meta).unwrap(); assert_eq!( - "".to_owned(), - type_option.decode_cell_data("1e".to_owned(), &field_meta).content + decode_cell_data(data, &field_meta, &field_meta.field_type) + .unwrap() + .content, + "".to_owned() ); } @@ -545,72 +548,72 @@ mod tests { } } - #[test] - fn date_description_apply_changeset_test() { - let mut type_option = DateTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - let date_timestamp = "1653609600".to_owned(); - - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: None, - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result.clone(), &field_meta).content; - assert_eq!(content, "May 27,2022".to_owned()); - - type_option.include_time = true; - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!(content, "May 27,2022 00:00".to_owned()); - - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:00".to_owned()), - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!(content, "May 27,2022 01:00".to_owned()); - - let changeset = DateCellContentChangeset { - date: Some(date_timestamp), - time: Some("1:00 am".to_owned()), - }; - type_option.time_format = TimeFormat::TwelveHour; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!(content, "May 27,2022 01:00 AM".to_owned()); - } - - #[test] - #[should_panic] - fn date_description_apply_changeset_error_test() { - let mut type_option = DateTypeOption::default(); - type_option.include_time = true; - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - let date_timestamp = "1653609600".to_owned(); - - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:a0".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); - - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); - } - - #[test] - #[should_panic] - fn date_description_invalid_data_test() { - let type_option = DateTypeOption::default(); - type_option.apply_changeset("he", None).unwrap(); - } - - fn data(s: i64) -> String { + // #[test] + // fn date_description_apply_changeset_test() { + // let mut type_option = DateTypeOption::default(); + // let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + // let date_timestamp = "1653609600".to_owned(); + // + // let changeset = DateCellContentChangeset { + // date: Some(date_timestamp.clone()), + // time: None, + // }; + // let result = type_option.apply_changeset(changeset, None).unwrap(); + // let content = type_option.decode_cell_data(result.clone(), &field_meta).content; + // assert_eq!(content, "May 27,2022".to_owned()); + // + // type_option.include_time = true; + // let content = type_option.decode_cell_data(result, &field_meta).content; + // assert_eq!(content, "May 27,2022 00:00".to_owned()); + // + // let changeset = DateCellContentChangeset { + // date: Some(date_timestamp.clone()), + // time: Some("1:00".to_owned()), + // }; + // let result = type_option.apply_changeset(changeset, None).unwrap(); + // let content = type_option.decode_cell_data(result, &field_meta).content; + // assert_eq!(content, "May 27,2022 01:00".to_owned()); + // + // let changeset = DateCellContentChangeset { + // date: Some(date_timestamp), + // time: Some("1:00 am".to_owned()), + // }; + // type_option.time_format = TimeFormat::TwelveHour; + // let result = type_option.apply_changeset(changeset, None).unwrap(); + // let content = type_option.decode_cell_data(result, &field_meta).content; + // assert_eq!(content, "May 27,2022 01:00 AM".to_owned()); + // } + // + // #[test] + // #[should_panic] + // fn date_description_apply_changeset_error_test() { + // let mut type_option = DateTypeOption::default(); + // type_option.include_time = true; + // let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + // let date_timestamp = "1653609600".to_owned(); + // + // let changeset = DateCellContentChangeset { + // date: Some(date_timestamp.clone()), + // time: Some("1:a0".to_owned()), + // }; + // let _ = type_option.apply_changeset(changeset, None).unwrap(); + // + // let changeset = DateCellContentChangeset { + // date: Some(date_timestamp.clone()), + // time: Some("1:".to_owned()), + // }; + // let _ = type_option.apply_changeset(changeset, None).unwrap(); + // } + // + // #[test] + // #[should_panic] + // fn date_description_invalid_data_test() { + // let type_option = DateTypeOption::default(); + // type_option.apply_changeset("he", None).unwrap(); + // } + // + fn data(s: i64) -> TypeOptionCellData { let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap(); - TypeOptionCellData::new(&json, FieldType::DateTime).json() + TypeOptionCellData::new(&json, FieldType::DateTime) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs index a57b056f5d..26f524b110 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs @@ -77,36 +77,37 @@ pub struct NumberTypeOption { impl_type_option!(NumberTypeOption, FieldType::Number); impl CellDataOperation for NumberTypeOption { - fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - if type_option_cell_data.is_date() { - return DecodedCellData::default(); + fn decode_cell_data>( + &self, + type_option_cell_data: T, + _field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + if type_option_cell_data.is_date() { + return DecodedCellData::default(); + } + + let cell_data = type_option_cell_data.data; + match self.format { + NumberFormat::Number => { + if let Ok(v) = cell_data.parse::() { + return DecodedCellData::from_content(v.to_string()); + } + + if let Ok(v) = cell_data.parse::() { + return DecodedCellData::from_content(v.to_string()); + } + + DecodedCellData::default() } - - let cell_data = type_option_cell_data.data; - match self.format { - NumberFormat::Number => { - if let Ok(v) = cell_data.parse::() { - return DecodedCellData::from_content(v.to_string()); - } - - if let Ok(v) = cell_data.parse::() { - return DecodedCellData::from_content(v.to_string()); - } - - DecodedCellData::default() - } - NumberFormat::Percent => { - let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); - DecodedCellData::from_content(content) - } - _ => { - let content = self.money_from_str(&cell_data); - DecodedCellData::from_content(content) - } + NumberFormat::Percent => { + let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); + DecodedCellData::from_content(content) + } + _ => { + let content = self.money_from_str(&cell_data); + DecodedCellData::from_content(content) } - } else { - DecodedCellData::default() } } @@ -125,7 +126,7 @@ impl CellDataOperation for NumberTypeOption { } } - Ok(TypeOptionCellData::new(&data, self.field_type()).json()) + Ok(data) } } @@ -615,163 +616,163 @@ fn make_strip_symbol() -> Vec { symbols } -#[cfg(test)] -mod tests { - use crate::services::field::FieldBuilder; - use crate::services::field::{NumberFormat, NumberTypeOption}; - use crate::services::row::{CellDataOperation, TypeOptionCellData}; - use flowy_grid_data_model::entities::FieldType; - use strum::IntoEnumIterator; - - #[test] - fn number_description_invalid_input_test() { - let type_option = NumberTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - assert_eq!( - "".to_owned(), - type_option.decode_cell_data(data(""), &field_meta).content - ); - assert_eq!( - "".to_owned(), - type_option.decode_cell_data(data("abc"), &field_meta).content - ); - } - - #[test] - fn number_description_test() { - let mut type_option = NumberTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned()); - assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned()); - assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned()); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Number => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "18443".to_owned() - ); - } - NumberFormat::USD => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "$18,443".to_owned() - ); - assert_eq!( - type_option.decode_cell_data(data(""), &field_meta).content, - "".to_owned() - ); - assert_eq!( - type_option.decode_cell_data(data("abc"), &field_meta).content, - "".to_owned() - ); - } - NumberFormat::Yen => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "¥18,443".to_owned() - ); - } - NumberFormat::Yuan => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "CN¥18,443".to_owned() - ); - } - NumberFormat::EUR => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "€18.443".to_owned() - ); - } - _ => {} - } - } - } - - fn data(s: &str) -> String { - TypeOptionCellData::new(s, FieldType::Number).json() - } - - #[test] - fn number_description_scale_test() { - let mut type_option = NumberTypeOption { - scale: 1, - ..Default::default() - }; - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Number => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "18443".to_owned() - ); - } - NumberFormat::USD => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "$1,844.3".to_owned() - ); - } - NumberFormat::Yen => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "¥1,844.3".to_owned() - ); - } - NumberFormat::EUR => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "€1.844,3".to_owned() - ); - } - _ => {} - } - } - } - - #[test] - fn number_description_sign_test() { - let mut type_option = NumberTypeOption { - sign_positive: false, - ..Default::default() - }; - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - - for format in NumberFormat::iter() { - type_option.format = format; - match format { - NumberFormat::Number => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "18443".to_owned() - ); - } - NumberFormat::USD => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "-$18,443".to_owned() - ); - } - NumberFormat::Yen => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "-¥18,443".to_owned() - ); - } - NumberFormat::EUR => { - assert_eq!( - type_option.decode_cell_data(data("18443"), &field_meta).content, - "-€18.443".to_owned() - ); - } - _ => {} - } - } - } -} +// #[cfg(test)] +// mod tests { +// use crate::services::field::FieldBuilder; +// use crate::services::field::{NumberFormat, NumberTypeOption}; +// use crate::services::row::{CellDataOperation, TypeOptionCellData}; +// use flowy_grid_data_model::entities::FieldType; +// use strum::IntoEnumIterator; +// +// #[test] +// fn number_description_invalid_input_test() { +// let type_option = NumberTypeOption::default(); +// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); +// assert_eq!( +// "".to_owned(), +// type_option.decode_cell_data(data(""), &field_meta).content +// ); +// assert_eq!( +// "".to_owned(), +// type_option.decode_cell_data(data("abc"), &field_meta).content +// ); +// } +// +// #[test] +// fn number_description_test() { +// let mut type_option = NumberTypeOption::default(); +// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); +// assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned()); +// assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned()); +// assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned()); +// +// for format in NumberFormat::iter() { +// type_option.format = format; +// match format { +// NumberFormat::Number => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "18443".to_owned() +// ); +// } +// NumberFormat::USD => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "$18,443".to_owned() +// ); +// assert_eq!( +// type_option.decode_cell_data(data(""), &field_meta).content, +// "".to_owned() +// ); +// assert_eq!( +// type_option.decode_cell_data(data("abc"), &field_meta).content, +// "".to_owned() +// ); +// } +// NumberFormat::Yen => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "¥18,443".to_owned() +// ); +// } +// NumberFormat::Yuan => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "CN¥18,443".to_owned() +// ); +// } +// NumberFormat::EUR => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "€18.443".to_owned() +// ); +// } +// _ => {} +// } +// } +// } +// +// fn data(s: &str) -> String { +// TypeOptionCellData::new(s, FieldType::Number).json() +// } +// +// #[test] +// fn number_description_scale_test() { +// let mut type_option = NumberTypeOption { +// scale: 1, +// ..Default::default() +// }; +// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); +// +// for format in NumberFormat::iter() { +// type_option.format = format; +// match format { +// NumberFormat::Number => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "18443".to_owned() +// ); +// } +// NumberFormat::USD => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "$1,844.3".to_owned() +// ); +// } +// NumberFormat::Yen => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "¥1,844.3".to_owned() +// ); +// } +// NumberFormat::EUR => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "€1.844,3".to_owned() +// ); +// } +// _ => {} +// } +// } +// } +// +// #[test] +// fn number_description_sign_test() { +// let mut type_option = NumberTypeOption { +// sign_positive: false, +// ..Default::default() +// }; +// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); +// +// for format in NumberFormat::iter() { +// type_option.format = format; +// match format { +// NumberFormat::Number => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "18443".to_owned() +// ); +// } +// NumberFormat::USD => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "-$18,443".to_owned() +// ); +// } +// NumberFormat::Yen => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "-¥18,443".to_owned() +// ); +// } +// NumberFormat::EUR => { +// assert_eq!( +// type_option.decode_cell_data(data("18443"), &field_meta).content, +// "-€18.443".to_owned() +// ); +// } +// _ => {} +// } +// } +// } +// } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs index 2ba96968ce..71aa697b6f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs @@ -96,18 +96,21 @@ impl SelectOptionOperation for SingleSelectTypeOption { } impl CellDataOperation for SingleSelectTypeOption { - fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - if !type_option_cell_data.is_single_select() { - return DecodedCellData::default(); - } + fn decode_cell_data>( + &self, + type_option_cell_data: T, + _field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + if !type_option_cell_data.is_select_option() { + return DecodedCellData::default(); + } - if let Some(option_id) = select_option_ids(type_option_cell_data.data).first() { - return match self.options.iter().find(|option| &option.id == option_id) { - None => DecodedCellData::default(), - Some(option) => DecodedCellData::from_content(option.name.clone()), - }; - } + if let Some(option_id) = select_option_ids(type_option_cell_data.data).first() { + return match self.options.iter().find(|option| &option.id == option_id) { + None => DecodedCellData::default(), + Some(option) => DecodedCellData::from_content(option.name.clone()), + }; } DecodedCellData::default() @@ -129,7 +132,7 @@ impl CellDataOperation for SingleSelectTypeOption { new_cell_data = "".to_string() } - Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json()) + Ok(new_cell_data) } } @@ -185,23 +188,26 @@ impl SelectOptionOperation for MultiSelectTypeOption { } impl CellDataOperation for MultiSelectTypeOption { - fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - if !type_option_cell_data.is_multi_select() { - return DecodedCellData::default(); - } - let option_ids = select_option_ids(type_option_cell_data.data); - let content = self - .options - .iter() - .filter(|option| option_ids.contains(&option.id)) - .map(|option| option.name.clone()) - .collect::>() - .join(SELECTION_IDS_SEPARATOR); - DecodedCellData::from_content(content) - } else { - DecodedCellData::default() + fn decode_cell_data>( + &self, + type_option_cell_data: T, + _field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + if !type_option_cell_data.is_select_option() { + return DecodedCellData::default(); } + + let option_ids = select_option_ids(type_option_cell_data.data); + let content = self + .options + .iter() + .filter(|option| option_ids.contains(&option.id)) + .map(|option| option.name.clone()) + .collect::>() + .join(SELECTION_IDS_SEPARATOR); + + DecodedCellData::from_content(content) } fn apply_changeset>( @@ -237,7 +243,7 @@ impl CellDataOperation for MultiSelectTypeOption { } } - Ok(TypeOptionCellData::new(&new_cell_data, self.field_type()).json()) + Ok(new_cell_data) } } @@ -485,108 +491,108 @@ fn make_select_context_from(cell_meta: &Option, options: &[SelectOptio } } -#[cfg(test)] -mod tests { - use crate::services::field::FieldBuilder; - use crate::services::field::{ - MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset, - SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, - }; - use crate::services::row::CellDataOperation; - - #[test] - fn single_select_test() { - let google_option = SelectOption::new("Google"); - let facebook_option = SelectOption::new("Facebook"); - let twitter_option = SelectOption::new("Twitter"); - let single_select = SingleSelectTypeOptionBuilder::default() - .option(google_option.clone()) - .option(facebook_option.clone()) - .option(twitter_option); - - let field_meta = FieldBuilder::new(single_select) - .name("Platform") - .visibility(true) - .build(); - - let type_option = SingleSelectTypeOption::from(&field_meta); - - let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR); - let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); - let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_eq!( - type_option.decode_cell_data(cell_data, &field_meta).content, - google_option.name, - ); - - let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); - let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_eq!( - type_option.decode_cell_data(cell_data, &field_meta).content, - google_option.name, - ); - - // Invalid option id - let cell_data = type_option - .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) - .unwrap(); - assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); - - // Invalid option id - let cell_data = type_option - .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None) - .unwrap(); - assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); - - // Invalid changeset - assert!(type_option.apply_changeset("123", None).is_err()); - } - - #[test] - fn multi_select_test() { - let google_option = SelectOption::new("Google"); - let facebook_option = SelectOption::new("Facebook"); - let twitter_option = SelectOption::new("Twitter"); - let multi_select = MultiSelectTypeOptionBuilder::default() - .option(google_option.clone()) - .option(facebook_option.clone()) - .option(twitter_option); - - let field_meta = FieldBuilder::new(multi_select) - .name("Platform") - .visibility(true) - .build(); - - let type_option = MultiSelectTypeOption::from(&field_meta); - - let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); - let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); - let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_eq!( - type_option.decode_cell_data(cell_data, &field_meta).content, - vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR), - ); - - let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); - let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_eq!( - type_option.decode_cell_data(cell_data, &field_meta).content, - google_option.name, - ); - - // Invalid option id - let cell_data = type_option - .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) - .unwrap(); - assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); - - // Invalid option id - let cell_data = type_option - .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None) - .unwrap(); - assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); - - // Invalid changeset - assert!(type_option.apply_changeset("123", None).is_err()); - } -} +// #[cfg(test)] +// mod tests { +// use crate::services::field::FieldBuilder; +// use crate::services::field::{ +// MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset, +// SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, +// }; +// use crate::services::row::CellDataOperation; +// +// #[test] +// fn single_select_test() { +// let google_option = SelectOption::new("Google"); +// let facebook_option = SelectOption::new("Facebook"); +// let twitter_option = SelectOption::new("Twitter"); +// let single_select = SingleSelectTypeOptionBuilder::default() +// .option(google_option.clone()) +// .option(facebook_option.clone()) +// .option(twitter_option); +// +// let field_meta = FieldBuilder::new(single_select) +// .name("Platform") +// .visibility(true) +// .build(); +// +// let type_option = SingleSelectTypeOption::from(&field_meta); +// +// let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR); +// let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); +// let cell_data = type_option.apply_changeset(data, None).unwrap(); +// assert_eq!( +// type_option.decode_cell_data(cell_data, &field_meta).content, +// google_option.name, +// ); +// +// let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); +// let cell_data = type_option.apply_changeset(data, None).unwrap(); +// assert_eq!( +// type_option.decode_cell_data(cell_data, &field_meta).content, +// google_option.name, +// ); +// +// // Invalid option id +// let cell_data = type_option +// .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) +// .unwrap(); +// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); +// +// // Invalid option id +// let cell_data = type_option +// .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None) +// .unwrap(); +// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); +// +// // Invalid changeset +// assert!(type_option.apply_changeset("123", None).is_err()); +// } +// +// #[test] +// fn multi_select_test() { +// let google_option = SelectOption::new("Google"); +// let facebook_option = SelectOption::new("Facebook"); +// let twitter_option = SelectOption::new("Twitter"); +// let multi_select = MultiSelectTypeOptionBuilder::default() +// .option(google_option.clone()) +// .option(facebook_option.clone()) +// .option(twitter_option); +// +// let field_meta = FieldBuilder::new(multi_select) +// .name("Platform") +// .visibility(true) +// .build(); +// +// let type_option = MultiSelectTypeOption::from(&field_meta); +// +// let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); +// let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); +// let cell_data = type_option.apply_changeset(data, None).unwrap(); +// assert_eq!( +// type_option.decode_cell_data(cell_data, &field_meta).content, +// vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR), +// ); +// +// let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); +// let cell_data = type_option.apply_changeset(data, None).unwrap(); +// assert_eq!( +// type_option.decode_cell_data(cell_data, &field_meta).content, +// google_option.name, +// ); +// +// // Invalid option id +// let cell_data = type_option +// .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) +// .unwrap(); +// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); +// +// // Invalid option id +// let cell_data = type_option +// .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None) +// .unwrap(); +// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); +// +// // Invalid changeset +// assert!(type_option.apply_changeset("123", None).is_err()); +// } +// } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index 348114e122..f24af8328b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -35,19 +35,21 @@ pub struct RichTextTypeOption { impl_type_option!(RichTextTypeOption, FieldType::RichText); impl CellDataOperation for RichTextTypeOption { - fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData { - if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) { - if type_option_cell_data.is_date() - || type_option_cell_data.is_single_select() - || type_option_cell_data.is_multi_select() - || type_option_cell_data.is_number() - { - decode_cell_data(data, field_meta, &type_option_cell_data.field_type).unwrap_or_default() - } else { - DecodedCellData::from_content(type_option_cell_data.data) - } + fn decode_cell_data>( + &self, + type_option_cell_data: T, + field_meta: &FieldMeta, + ) -> DecodedCellData { + let type_option_cell_data = type_option_cell_data.into(); + if type_option_cell_data.is_date() + || type_option_cell_data.is_single_select() + || type_option_cell_data.is_multi_select() + || type_option_cell_data.is_number() + { + let field_type = type_option_cell_data.field_type.clone(); + decode_cell_data(type_option_cell_data, field_meta, &field_type).unwrap_or_default() } else { - DecodedCellData::default() + DecodedCellData::from_content(type_option_cell_data.data) } } @@ -60,69 +62,69 @@ impl CellDataOperation for RichTextTypeOption { if data.len() > 10000 { Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000")) } else { - Ok(TypeOptionCellData::new(&data, self.field_type()).json()) + Ok(data.to_string()) } } } -#[cfg(test)] -mod tests { - use crate::services::field::FieldBuilder; - use crate::services::field::*; - use crate::services::row::{CellDataOperation, TypeOptionCellData}; - use flowy_grid_data_model::entities::FieldType; - - #[test] - fn text_description_test() { - let type_option = RichTextTypeOption::default(); - - // date - let date_time_field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); - let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); - let data = TypeOptionCellData::new(&json, FieldType::DateTime).json(); - assert_eq!( - type_option.decode_cell_data(data, &date_time_field_meta).content, - "Mar 14,2022".to_owned() - ); - - // Single select - let done_option = SelectOption::new("Done"); - let done_option_id = done_option.id.clone(); - let single_select = SingleSelectTypeOptionBuilder::default().option(done_option); - let single_select_field_meta = FieldBuilder::new(single_select).build(); - let cell_data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json(); - assert_eq!( - type_option - .decode_cell_data(cell_data, &single_select_field_meta) - .content, - "Done".to_owned() - ); - - // Multiple select - let google_option = SelectOption::new("Google"); - let facebook_option = SelectOption::new("Facebook"); - let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); - let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str(); - let multi_select = MultiSelectTypeOptionBuilder::default() - .option(google_option) - .option(facebook_option); - let multi_select_field_meta = FieldBuilder::new(multi_select).build(); - let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta); - let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap(); - assert_eq!( - type_option - .decode_cell_data(cell_data, &multi_select_field_meta) - .content, - "Google,Facebook".to_owned() - ); - - //Number - let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field_meta = FieldBuilder::new(number).build(); - let data = TypeOptionCellData::new("18443", FieldType::Number).json(); - assert_eq!( - type_option.decode_cell_data(data, &number_field_meta).content, - "$18,443".to_owned() - ); - } -} +// #[cfg(test)] +// mod tests { +// use crate::services::field::FieldBuilder; +// use crate::services::field::*; +// use crate::services::row::{CellDataOperation, TypeOptionCellData}; +// use flowy_grid_data_model::entities::FieldType; +// +// #[test] +// fn text_description_test() { +// let type_option = RichTextTypeOption::default(); +// +// // date +// let date_time_field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); +// let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); +// let data = TypeOptionCellData::new(&json, FieldType::DateTime).json(); +// assert_eq!( +// type_option.decode_cell_data(data, &date_time_field_meta).content, +// "Mar 14,2022".to_owned() +// ); +// +// // Single select +// let done_option = SelectOption::new("Done"); +// let done_option_id = done_option.id.clone(); +// let single_select = SingleSelectTypeOptionBuilder::default().option(done_option); +// let single_select_field_meta = FieldBuilder::new(single_select).build(); +// let cell_data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json(); +// assert_eq!( +// type_option +// .decode_cell_data(cell_data, &single_select_field_meta) +// .content, +// "Done".to_owned() +// ); +// +// // Multiple select +// let google_option = SelectOption::new("Google"); +// let facebook_option = SelectOption::new("Facebook"); +// let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); +// let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str(); +// let multi_select = MultiSelectTypeOptionBuilder::default() +// .option(google_option) +// .option(facebook_option); +// let multi_select_field_meta = FieldBuilder::new(multi_select).build(); +// let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta); +// let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap(); +// assert_eq!( +// type_option +// .decode_cell_data(cell_data, &multi_select_field_meta) +// .content, +// "Google,Facebook".to_owned() +// ); +// +// //Number +// let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); +// let number_field_meta = FieldBuilder::new(number).build(); +// let data = TypeOptionCellData::new("18443", FieldType::Number).json(); +// assert_eq!( +// type_option.decode_cell_data(data, &number_field_meta).content, +// "$18,443".to_owned() +// ); +// } +// } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs index 5f6dbc74fd..73513d29f8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs @@ -3,9 +3,10 @@ use flowy_error::FlowyError; use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType}; use serde::{Deserialize, Serialize}; use std::fmt::Formatter; +use std::str::FromStr; pub trait CellDataOperation { - fn decode_cell_data(&self, data: String, field_meta: &FieldMeta) -> DecodedCellData; + fn decode_cell_data>(&self, data: T, field_meta: &FieldMeta) -> DecodedCellData; fn apply_changeset>( &self, changeset: T, @@ -52,6 +53,22 @@ impl std::str::FromStr for TypeOptionCellData { } } +impl std::convert::TryInto for String { + type Error = FlowyError; + + fn try_into(self) -> Result { + TypeOptionCellData::from_str(&self) + } +} + +// impl std::convert::Into for String { +// type Error = FlowyError; +// +// fn try_into(self) -> Result { +// TypeOptionCellData::from_str(&self) +// } +// } + impl TypeOptionCellData { pub fn new(data: T, field_type: FieldType) -> Self { TypeOptionCellData { @@ -87,6 +104,10 @@ impl TypeOptionCellData { pub fn is_multi_select(&self) -> bool { self.field_type == FieldType::MultiSelect } + + pub fn is_select_option(&self) -> bool { + self.field_type == FieldType::MultiSelect || self.field_type == FieldType::SingleSelect + } } /// The changeset will be deserialized into specific data base on the FieldType. @@ -96,47 +117,57 @@ pub fn apply_cell_data_changeset>( cell_meta: Option, field_meta: &FieldMeta, ) -> Result { - match field_meta.field_type { + let s = match field_meta.field_type { FieldType::RichText => RichTextTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Number => NumberTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::DateTime => DateTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), - } + }?; + + Ok(TypeOptionCellData::new(s, field_meta.field_type.clone()).json()) } -pub fn decode_cell_data(data: String, field_meta: &FieldMeta, field_type: &FieldType) -> Option { - let s = match field_type { - FieldType::RichText => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - FieldType::Number => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - FieldType::DateTime => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - FieldType::SingleSelect => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - FieldType::MultiSelect => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - FieldType::Checkbox => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(data, field_meta), - }; - tracing::Span::current().record( - "content", - &format!("{:?}: {}", field_meta.field_type, s.content).as_str(), - ); - Some(s) +pub fn decode_cell_data>( + data: T, + field_meta: &FieldMeta, + field_type: &FieldType, +) -> Option { + if let Ok(type_option_cell_data) = data.try_into() { + let s = match field_type { + FieldType::RichText => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + FieldType::Number => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + FieldType::DateTime => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + FieldType::SingleSelect => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + FieldType::MultiSelect => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + FieldType::Checkbox => field_meta + .get_type_option_entry::(field_type)? + .decode_cell_data(type_option_cell_data, field_meta), + }; + tracing::Span::current().record( + "content", + &format!("{:?}: {}", field_meta.field_type, s.content).as_str(), + ); + Some(s) + } else { + Some(DecodedCellData::default()) + } } #[derive(Default)] pub struct DecodedCellData { - pub raw: String, + raw: String, pub content: String, } From 5b2b50dc9cdaf817968a04cfa7651e3f91d5a16c Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 23 May 2022 22:53:13 +0800 Subject: [PATCH 6/7] refactor: cell data operation --- .../type_options/checkbox_type_option.rs | 14 +++---- .../field/type_options/date_type_option.rs | 26 ++++++------ .../field/type_options/number_type_option.rs | 14 +++---- .../type_options/selection_type_option.rs | 28 ++++++------- .../field/type_options/text_type_option.rs | 22 +++++----- .../src/services/row/cell_data_operation.rs | 40 +++++++++++++------ 6 files changed, 80 insertions(+), 64 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs index a3c084de03..111715ba6b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs @@ -43,14 +43,15 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); const YES: &str = "Yes"; const NO: &str = "No"; -impl CellDataOperation for CheckboxTypeOption { +impl CellDataOperation for CheckboxTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, _field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); - if !type_option_cell_data.is_checkbox() { + if !decoded_field_type.is_checkbox() { return DecodedCellData::default(); } let cell_data = type_option_cell_data.data; @@ -61,11 +62,10 @@ impl CellDataOperation for CheckboxTypeOption { DecodedCellData::default() } - fn apply_changeset>( - &self, - changeset: T, - _cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { let changeset = changeset.into(); let s = match string_to_bool(&changeset) { true => YES, diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 3dcc1d81eb..62e4887746 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -135,10 +135,11 @@ impl DateTypeOption { } } -impl CellDataOperation for DateTypeOption { +impl CellDataOperation for DateTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, _field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); @@ -146,7 +147,7 @@ impl CellDataOperation for DateTypeOption { // It happens when switching from one field to another. // For example: // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. - if !type_option_cell_data.is_date() { + if !decoded_field_type.is_date() { return DecodedCellData::default(); } match DateCellDataSerde::from_str(&type_option_cell_data.data) { @@ -155,11 +156,10 @@ impl CellDataOperation for DateTypeOption { } } - fn apply_changeset>( - &self, - changeset: T, - _cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?; let cell_data = match content_changeset.date_timestamp() { None => DateCellDataSerde::default(), @@ -174,7 +174,7 @@ impl CellDataOperation for DateTypeOption { }, }; - Ok(cell_data.to_string()) + Ok(cell_data) } } @@ -316,15 +316,17 @@ impl DateCellDataSerde { Self { timestamp, time } } - fn to_string(self) -> String { - serde_json::to_string(&self).unwrap_or("".to_string()) - } - fn from_str(s: &str) -> FlowyResult { serde_json::from_str::(s).map_err(internal_error) } } +impl ToString for DateCellDataSerde { + fn to_string(&self) -> String { + serde_json::to_string(&self).unwrap_or("".to_string()) + } +} + fn default_time_str(time_format: &TimeFormat) -> String { match time_format { TimeFormat::TwelveHour => "12:00 AM".to_string(), diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs index 26f524b110..b97a3ca092 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs @@ -76,14 +76,15 @@ pub struct NumberTypeOption { } impl_type_option!(NumberTypeOption, FieldType::Number); -impl CellDataOperation for NumberTypeOption { +impl CellDataOperation for NumberTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, _field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); - if type_option_cell_data.is_date() { + if decoded_field_type.is_date() { return DecodedCellData::default(); } @@ -111,11 +112,10 @@ impl CellDataOperation for NumberTypeOption { } } - fn apply_changeset>( - &self, - changeset: T, - _cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { let changeset = changeset.into(); let mut data = changeset.trim().to_string(); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs index 71aa697b6f..3d9cee701f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs @@ -95,14 +95,15 @@ impl SelectOptionOperation for SingleSelectTypeOption { } } -impl CellDataOperation for SingleSelectTypeOption { +impl CellDataOperation for SingleSelectTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, _field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); - if !type_option_cell_data.is_select_option() { + if !decoded_field_type.is_select_option() { return DecodedCellData::default(); } @@ -116,11 +117,10 @@ impl CellDataOperation for SingleSelectTypeOption { DecodedCellData::default() } - fn apply_changeset>( - &self, - changeset: T, - _cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { let changeset = changeset.into(); let select_option_changeset: SelectOptionCellContentChangeset = serde_json::from_str(&changeset)?; let new_cell_data: String; @@ -187,14 +187,15 @@ impl SelectOptionOperation for MultiSelectTypeOption { } } -impl CellDataOperation for MultiSelectTypeOption { +impl CellDataOperation for MultiSelectTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, _field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); - if !type_option_cell_data.is_select_option() { + if !decoded_field_type.is_select_option() { return DecodedCellData::default(); } @@ -210,11 +211,10 @@ impl CellDataOperation for MultiSelectTypeOption { DecodedCellData::from_content(content) } - fn apply_changeset>( - &self, - changeset: T, - cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: T, cell_meta: Option) -> Result + where + T: Into, + { let content_changeset: SelectOptionCellContentChangeset = serde_json::from_str(&changeset.into())?; let new_cell_data: String; match cell_meta { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index f24af8328b..7cf4fc3d3e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -34,17 +34,18 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); -impl CellDataOperation for RichTextTypeOption { +impl CellDataOperation for RichTextTypeOption { fn decode_cell_data>( &self, type_option_cell_data: T, + decoded_field_type: &FieldType, field_meta: &FieldMeta, ) -> DecodedCellData { let type_option_cell_data = type_option_cell_data.into(); - if type_option_cell_data.is_date() - || type_option_cell_data.is_single_select() - || type_option_cell_data.is_multi_select() - || type_option_cell_data.is_number() + if decoded_field_type.is_date() + || decoded_field_type.is_single_select() + || decoded_field_type.is_multi_select() + || decoded_field_type.is_number() { let field_type = type_option_cell_data.field_type.clone(); decode_cell_data(type_option_cell_data, field_meta, &field_type).unwrap_or_default() @@ -53,16 +54,15 @@ impl CellDataOperation for RichTextTypeOption { } } - fn apply_changeset>( - &self, - changeset: T, - _cell_meta: Option, - ) -> Result { + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { let data = changeset.into(); if data.len() > 10000 { Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000")) } else { - Ok(data.to_string()) + Ok(data.0) } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs index 73513d29f8..2bb78a0bf0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs @@ -5,17 +5,22 @@ use serde::{Deserialize, Serialize}; use std::fmt::Formatter; use std::str::FromStr; -pub trait CellDataOperation { - fn decode_cell_data>(&self, data: T, field_meta: &FieldMeta) -> DecodedCellData; - fn apply_changeset>( +pub trait CellDataOperation { + fn decode_cell_data>( &self, - changeset: T, + data: T, + decoded_field_type: &FieldType, + field_meta: &FieldMeta, + ) -> DecodedCellData; + fn apply_changeset>( + &self, + changeset: C, cell_meta: Option, - ) -> Result; + ) -> Result; } #[derive(Debug)] -pub struct CellContentChangeset(String); +pub struct CellContentChangeset(pub String); impl std::fmt::Display for CellContentChangeset { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -44,6 +49,12 @@ pub struct TypeOptionCellData { pub field_type: FieldType, } +impl TypeOptionCellData { + pub fn split(self) -> (String, FieldType) { + (self.data, self.field_type) + } +} + impl std::str::FromStr for TypeOptionCellData { type Err = FlowyError; @@ -120,7 +131,9 @@ pub fn apply_cell_data_changeset>( let s = match field_meta.field_type { FieldType::RichText => RichTextTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Number => NumberTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), - FieldType::DateTime => DateTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), + FieldType::DateTime => DateTypeOption::from(field_meta) + .apply_changeset(changeset, cell_meta) + .map(|data| data.to_string()), FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), @@ -135,25 +148,26 @@ pub fn decode_cell_data>( field_type: &FieldType, ) -> Option { if let Ok(type_option_cell_data) = data.try_into() { + let (decoded_field_type, data) = &type_option_cell_data.split(); let s = match field_type { FieldType::RichText => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), FieldType::Number => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), FieldType::DateTime => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), FieldType::SingleSelect => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), FieldType::MultiSelect => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), FieldType::Checkbox => field_meta .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, field_meta), + .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), }; tracing::Span::current().record( "content", From 8c7b0bd5a738f5f9e7106a0d1d53916d5337342f Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 24 May 2022 14:56:55 +0800 Subject: [PATCH 7/7] fix: unit test --- .../type_options/checkbox_type_option.rs | 60 ++- .../field/type_options/date_type_option.rs | 346 +++++++++++------- .../field/type_options/number_type_option.rs | 319 ++++++++-------- .../type_options/selection_type_option.rs | 290 ++++++++------- .../field/type_options/text_type_option.rs | 154 ++++---- .../src/services/row/cell_data_operation.rs | 129 ++++--- .../flowy-grid/src/services/row/row_loader.rs | 8 +- .../flowy-grid/tests/grid/grid_test.rs | 5 +- .../src/entities/grid.rs | 28 ++ .../src/code_gen/protobuf_file/mod.rs | 23 +- 10 files changed, 755 insertions(+), 607 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs index 111715ba6b..90bb53cea6 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs @@ -1,15 +1,14 @@ use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; use bytes::Bytes; use flowy_derive::ProtoBuf; -use flowy_error::FlowyError; +use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; use serde::{Deserialize, Serialize}; -use std::str::FromStr; #[derive(Default)] pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption); @@ -43,23 +42,26 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox); const YES: &str = "Yes"; const NO: &str = "No"; -impl CellDataOperation for CheckboxTypeOption { - fn decode_cell_data>( +impl CellDataOperation for CheckboxTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, _field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into, + { if !decoded_field_type.is_checkbox() { - return DecodedCellData::default(); - } - let cell_data = type_option_cell_data.data; - if cell_data == YES || cell_data == NO { - return DecodedCellData::from_content(cell_data); + return Ok(DecodedCellData::default()); } - DecodedCellData::default() + let encoded_data = encoded_data.into(); + if encoded_data == YES || encoded_data == NO { + return Ok(DecodedCellData::from_content(encoded_data)); + } + + Ok(DecodedCellData::default()) } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -91,10 +93,10 @@ fn string_to_bool(bool_str: &str) -> bool { #[cfg(test)] mod tests { use crate::services::field::type_options::checkbox_type_option::{NO, YES}; - use crate::services::field::CheckboxTypeOption; + use crate::services::field::FieldBuilder; - use crate::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation}; - use diesel::types::IsNull::No; + use crate::services::row::{apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data}; + use flowy_grid_data_model::entities::FieldType; #[test] @@ -102,49 +104,37 @@ mod tests { let field_meta = FieldBuilder::from_field_type(&FieldType::Checkbox).build(); let data = apply_cell_data_changeset("true", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, YES ); let data = apply_cell_data_changeset("1", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, YES ); let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, YES ); let data = apply_cell_data_changeset("false", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, NO ); let data = apply_cell_data_changeset("no", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, NO ); let data = apply_cell_data_changeset("12", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, NO ); } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs index 62e4887746..5ecca97105 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs @@ -1,7 +1,9 @@ use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData}; +use crate::services::row::{ + CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData, TypeOptionCellData, +}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; use chrono::NaiveDateTime; @@ -90,10 +92,10 @@ impl DateTypeOption { let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?; let date = self.decode_cell_data_from_timestamp(&serde_cell_data).content; - let time = serde_cell_data.time.unwrap_or("".to_owned()); + let time = serde_cell_data.time.unwrap_or_else(|| "".to_owned()); let timestamp = serde_cell_data.timestamp; - return Ok(DateCellData { date, time, timestamp }); + Ok(DateCellData { date, time, timestamp }) } fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData { @@ -102,7 +104,7 @@ impl DateTypeOption { } let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time); - return DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content); + DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content) } fn timestamp_from_utc_with_time( @@ -131,29 +133,30 @@ impl DateTypeOption { } } - return Ok(utc.timestamp()); + Ok(utc.timestamp()) } } -impl CellDataOperation for DateTypeOption { - fn decode_cell_data>( +impl CellDataOperation, DateCellDataSerde> for DateTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, _field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into>, + { // Return default data if the type_option_cell_data is not FieldType::DateTime. // It happens when switching from one field to another. // For example: // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen. if !decoded_field_type.is_date() { - return DecodedCellData::default(); - } - match DateCellDataSerde::from_str(&type_option_cell_data.data) { - Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(&serde_cell_data), - Err(_) => DecodedCellData::default(), + return Ok(DecodedCellData::default()); } + + let encoded_data = encoded_data.into().try_into_inner()?; + Ok(self.decode_cell_data_from_timestamp(&encoded_data)) } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -308,22 +311,26 @@ impl DateCellDataSerde { fn new(timestamp: i64, time: Option, time_format: &TimeFormat) -> Self { Self { timestamp, - time: Some(time.unwrap_or(default_time_str(time_format))), + time: Some(time.unwrap_or_else(|| default_time_str(time_format))), } } pub(crate) fn from_timestamp(timestamp: i64, time: Option) -> Self { Self { timestamp, time } } +} - fn from_str(s: &str) -> FlowyResult { +impl FromStr for DateCellDataSerde { + type Err = FlowyError; + + fn from_str(s: &str) -> Result { serde_json::from_str::(s).map_err(internal_error) } } impl ToString for DateCellDataSerde { fn to_string(&self) -> String { - serde_json::to_string(&self).unwrap_or("".to_string()) + serde_json::to_string(&self).unwrap_or_else(|_| "".to_string()) } } @@ -410,11 +417,11 @@ impl std::convert::From for CellContentChangeset { #[cfg(test)] mod tests { use crate::services::field::FieldBuilder; - use crate::services::field::{ - DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, + use crate::services::field::{DateCellContentChangeset, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat}; + use crate::services::row::{ + apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data, CellDataOperation, EncodedCellData, }; - use crate::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation, TypeOptionCellData}; - use flowy_grid_data_model::entities::FieldType; + use flowy_grid_data_model::entities::{FieldMeta, FieldType}; use strum::IntoEnumIterator; #[test] @@ -422,9 +429,7 @@ mod tests { let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); let data = apply_cell_data_changeset("1e", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data(data, &field_meta, &field_meta.field_type) - .unwrap() - .content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, "".to_owned() ); } @@ -432,32 +437,44 @@ mod tests { #[test] fn date_description_date_format_test() { let mut type_option = DateTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); for date_format in DateFormat::iter() { type_option.date_format = date_format; match date_format { DateFormat::Friendly => { assert_eq!( "Mar 14,2022".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } DateFormat::US => { assert_eq!( "2022/03/14".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } DateFormat::ISO => { assert_eq!( "2022-03-14".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } DateFormat::Local => { assert_eq!( "2022/03/14".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } } @@ -467,7 +484,7 @@ mod tests { #[test] fn date_description_time_format_test() { let mut type_option = DateTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); for time_format in TimeFormat::iter() { type_option.time_format = time_format; match time_format { @@ -478,7 +495,10 @@ mod tests { ); assert_eq!( "Mar 14,2022".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } TimeFormat::TwelveHour => { @@ -488,7 +508,10 @@ mod tests { ); assert_eq!( "Mar 14,2022".to_owned(), - type_option.decode_cell_data(data(1647251762), &field_meta).content + type_option + .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) + .unwrap() + .content ); } } @@ -498,124 +521,171 @@ mod tests { #[test] fn date_description_time_format_test2() { let mut type_option = DateTypeOption::default(); - let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); + let field_type = FieldType::DateTime; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + for time_format in TimeFormat::iter() { type_option.time_format = time_format; type_option.include_time = true; match time_format { TimeFormat::TwentyFourHour => { - let changeset = DateCellContentChangeset { - date: Some(1653609600.to_string()), - time: None, - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!("May 27,2022 00:00".to_owned(), content); - - let changeset = DateCellContentChangeset { - date: Some(1653609600.to_string()), - time: Some("23:00".to_owned()), - }; - - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!("May 27,2022 23:00".to_owned(), content); + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(1653609600.to_string()), + time: None, + }, + &field_type, + &field_meta, + "May 27,2022 00:00", + ); + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(1653609600.to_string()), + time: Some("23:00".to_owned()), + }, + &field_type, + &field_meta, + "May 27,2022 23:00", + ); } TimeFormat::TwelveHour => { - let changeset = DateCellContentChangeset { - date: Some(1653609600.to_string()), - time: None, - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!("May 27,2022 12:00 AM".to_owned(), content); + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(1653609600.to_string()), + time: None, + }, + &field_type, + &field_meta, + "May 27,2022 12:00 AM", + ); - let changeset = DateCellContentChangeset { - date: Some(1653609600.to_string()), - time: Some("".to_owned()), - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!("May 27,2022".to_owned(), content); + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(1653609600.to_string()), + time: Some("".to_owned()), + }, + &field_type, + &field_meta, + "May 27,2022", + ); - let changeset = DateCellContentChangeset { - date: Some(1653609600.to_string()), - time: Some("11:23 pm".to_owned()), - }; - let result = type_option.apply_changeset(changeset, None).unwrap(); - let content = type_option.decode_cell_data(result, &field_meta).content; - assert_eq!("May 27,2022 11:23 PM".to_owned(), content); + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(1653609600.to_string()), + time: Some("11:23 pm".to_owned()), + }, + &field_type, + &field_meta, + "May 27,2022 11:23 PM", + ); } } } } - // #[test] - // fn date_description_apply_changeset_test() { - // let mut type_option = DateTypeOption::default(); - // let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - // let date_timestamp = "1653609600".to_owned(); - // - // let changeset = DateCellContentChangeset { - // date: Some(date_timestamp.clone()), - // time: None, - // }; - // let result = type_option.apply_changeset(changeset, None).unwrap(); - // let content = type_option.decode_cell_data(result.clone(), &field_meta).content; - // assert_eq!(content, "May 27,2022".to_owned()); - // - // type_option.include_time = true; - // let content = type_option.decode_cell_data(result, &field_meta).content; - // assert_eq!(content, "May 27,2022 00:00".to_owned()); - // - // let changeset = DateCellContentChangeset { - // date: Some(date_timestamp.clone()), - // time: Some("1:00".to_owned()), - // }; - // let result = type_option.apply_changeset(changeset, None).unwrap(); - // let content = type_option.decode_cell_data(result, &field_meta).content; - // assert_eq!(content, "May 27,2022 01:00".to_owned()); - // - // let changeset = DateCellContentChangeset { - // date: Some(date_timestamp), - // time: Some("1:00 am".to_owned()), - // }; - // type_option.time_format = TimeFormat::TwelveHour; - // let result = type_option.apply_changeset(changeset, None).unwrap(); - // let content = type_option.decode_cell_data(result, &field_meta).content; - // assert_eq!(content, "May 27,2022 01:00 AM".to_owned()); - // } - // - // #[test] - // #[should_panic] - // fn date_description_apply_changeset_error_test() { - // let mut type_option = DateTypeOption::default(); - // type_option.include_time = true; - // let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); - // let date_timestamp = "1653609600".to_owned(); - // - // let changeset = DateCellContentChangeset { - // date: Some(date_timestamp.clone()), - // time: Some("1:a0".to_owned()), - // }; - // let _ = type_option.apply_changeset(changeset, None).unwrap(); - // - // let changeset = DateCellContentChangeset { - // date: Some(date_timestamp.clone()), - // time: Some("1:".to_owned()), - // }; - // let _ = type_option.apply_changeset(changeset, None).unwrap(); - // } - // - // #[test] - // #[should_panic] - // fn date_description_invalid_data_test() { - // let type_option = DateTypeOption::default(); - // type_option.apply_changeset("he", None).unwrap(); - // } - // - fn data(s: i64) -> TypeOptionCellData { - let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap(); - TypeOptionCellData::new(&json, FieldType::DateTime) + #[test] + fn date_description_apply_changeset_test() { + let mut type_option = DateTypeOption::default(); + let field_type = FieldType::DateTime; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: None, + }, + &field_type, + &field_meta, + "May 27,2022", + ); + + type_option.include_time = true; + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: None, + }, + &field_type, + &field_meta, + "May 27,2022 00:00", + ); + + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00".to_owned()), + }, + &field_type, + &field_meta, + "May 27,2022 01:00", + ); + + type_option.time_format = TimeFormat::TwelveHour; + assert_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp), + time: Some("1:00 am".to_owned()), + }, + &field_type, + &field_meta, + "May 27,2022 01:00 AM", + ); + } + + #[test] + #[should_panic] + fn date_description_apply_changeset_error_test() { + let mut type_option = DateTypeOption::default(); + type_option.include_time = true; + let _field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let date_timestamp = "1653609600".to_owned(); + + let changeset = DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:a0".to_owned()), + }; + let _ = type_option.apply_changeset(changeset, None).unwrap(); + + let changeset = DateCellContentChangeset { + date: Some(date_timestamp), + time: Some("1:".to_owned()), + }; + let _ = type_option.apply_changeset(changeset, None).unwrap(); + } + + #[test] + #[should_panic] + fn date_description_invalid_data_test() { + let type_option = DateTypeOption::default(); + type_option.apply_changeset("he", None).unwrap(); + } + + fn data(s: i64) -> String { + serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap() + } + + fn assert_result( + type_option: &DateTypeOption, + changeset: DateCellContentChangeset, + field_type: &FieldType, + field_meta: &FieldMeta, + expected: &str, + ) { + let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap())); + let content = type_option + .decode_cell_data(encoded_data, field_type, field_meta) + .unwrap() + .content; + assert_eq!(expected.to_owned(), content); } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs index b97a3ca092..d500bbcc39 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs @@ -1,9 +1,9 @@ use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; use bytes::Bytes; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use flowy_error::FlowyError; +use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; @@ -76,38 +76,40 @@ pub struct NumberTypeOption { } impl_type_option!(NumberTypeOption, FieldType::Number); -impl CellDataOperation for NumberTypeOption { - fn decode_cell_data>( +impl CellDataOperation for NumberTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, _field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into, + { if decoded_field_type.is_date() { - return DecodedCellData::default(); + return Ok(DecodedCellData::default()); } - let cell_data = type_option_cell_data.data; + let cell_data = encoded_data.into(); match self.format { NumberFormat::Number => { if let Ok(v) = cell_data.parse::() { - return DecodedCellData::from_content(v.to_string()); + return Ok(DecodedCellData::from_content(v.to_string())); } if let Ok(v) = cell_data.parse::() { - return DecodedCellData::from_content(v.to_string()); + return Ok(DecodedCellData::from_content(v.to_string())); } - DecodedCellData::default() + Ok(DecodedCellData::default()) } NumberFormat::Percent => { let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); - DecodedCellData::from_content(content) + Ok(DecodedCellData::from_content(content)) } _ => { let content = self.money_from_str(&cell_data); - DecodedCellData::from_content(content) + Ok(DecodedCellData::from_content(content)) } } } @@ -616,163 +618,132 @@ fn make_strip_symbol() -> Vec { symbols } -// #[cfg(test)] -// mod tests { -// use crate::services::field::FieldBuilder; -// use crate::services::field::{NumberFormat, NumberTypeOption}; -// use crate::services::row::{CellDataOperation, TypeOptionCellData}; -// use flowy_grid_data_model::entities::FieldType; -// use strum::IntoEnumIterator; -// -// #[test] -// fn number_description_invalid_input_test() { -// let type_option = NumberTypeOption::default(); -// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); -// assert_eq!( -// "".to_owned(), -// type_option.decode_cell_data(data(""), &field_meta).content -// ); -// assert_eq!( -// "".to_owned(), -// type_option.decode_cell_data(data("abc"), &field_meta).content -// ); -// } -// -// #[test] -// fn number_description_test() { -// let mut type_option = NumberTypeOption::default(); -// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); -// assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned()); -// assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned()); -// assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned()); -// -// for format in NumberFormat::iter() { -// type_option.format = format; -// match format { -// NumberFormat::Number => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "18443".to_owned() -// ); -// } -// NumberFormat::USD => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "$18,443".to_owned() -// ); -// assert_eq!( -// type_option.decode_cell_data(data(""), &field_meta).content, -// "".to_owned() -// ); -// assert_eq!( -// type_option.decode_cell_data(data("abc"), &field_meta).content, -// "".to_owned() -// ); -// } -// NumberFormat::Yen => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "¥18,443".to_owned() -// ); -// } -// NumberFormat::Yuan => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "CN¥18,443".to_owned() -// ); -// } -// NumberFormat::EUR => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "€18.443".to_owned() -// ); -// } -// _ => {} -// } -// } -// } -// -// fn data(s: &str) -> String { -// TypeOptionCellData::new(s, FieldType::Number).json() -// } -// -// #[test] -// fn number_description_scale_test() { -// let mut type_option = NumberTypeOption { -// scale: 1, -// ..Default::default() -// }; -// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); -// -// for format in NumberFormat::iter() { -// type_option.format = format; -// match format { -// NumberFormat::Number => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "18443".to_owned() -// ); -// } -// NumberFormat::USD => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "$1,844.3".to_owned() -// ); -// } -// NumberFormat::Yen => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "¥1,844.3".to_owned() -// ); -// } -// NumberFormat::EUR => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "€1.844,3".to_owned() -// ); -// } -// _ => {} -// } -// } -// } -// -// #[test] -// fn number_description_sign_test() { -// let mut type_option = NumberTypeOption { -// sign_positive: false, -// ..Default::default() -// }; -// let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build(); -// -// for format in NumberFormat::iter() { -// type_option.format = format; -// match format { -// NumberFormat::Number => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "18443".to_owned() -// ); -// } -// NumberFormat::USD => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "-$18,443".to_owned() -// ); -// } -// NumberFormat::Yen => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "-¥18,443".to_owned() -// ); -// } -// NumberFormat::EUR => { -// assert_eq!( -// type_option.decode_cell_data(data("18443"), &field_meta).content, -// "-€18.443".to_owned() -// ); -// } -// _ => {} -// } -// } -// } -// } +#[cfg(test)] +mod tests { + use crate::services::field::FieldBuilder; + use crate::services::field::{NumberFormat, NumberTypeOption}; + use crate::services::row::CellDataOperation; + use flowy_grid_data_model::entities::{FieldMeta, FieldType}; + use strum::IntoEnumIterator; + + #[test] + fn number_description_invalid_input_test() { + let type_option = NumberTypeOption::default(); + let field_type = FieldType::Number; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + assert_equal(&type_option, "", "", &field_type, &field_meta); + assert_equal(&type_option, "abc", "", &field_type, &field_meta); + } + + #[test] + fn number_description_test() { + let mut type_option = NumberTypeOption::default(); + let field_type = FieldType::Number; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + assert_eq!(type_option.strip_symbol("¥18,443"), "18443".to_owned()); + assert_eq!(type_option.strip_symbol("$18,443"), "18443".to_owned()); + assert_eq!(type_option.strip_symbol("€18.443"), "18443".to_owned()); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Number => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_meta); + } + NumberFormat::USD => { + assert_equal(&type_option, "18443", "$18,443", &field_type, &field_meta); + assert_equal(&type_option, "", "", &field_type, &field_meta); + assert_equal(&type_option, "abc", "", &field_type, &field_meta); + } + NumberFormat::Yen => { + assert_equal(&type_option, "18443", "¥18,443", &field_type, &field_meta); + } + NumberFormat::Yuan => { + assert_equal(&type_option, "18443", "CN¥18,443", &field_type, &field_meta); + } + NumberFormat::EUR => { + assert_equal(&type_option, "18443", "€18.443", &field_type, &field_meta); + } + _ => {} + } + } + } + + #[test] + fn number_description_scale_test() { + let mut type_option = NumberTypeOption { + scale: 1, + ..Default::default() + }; + let field_type = FieldType::Number; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Number => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_meta); + } + NumberFormat::USD => { + assert_equal(&type_option, "18443", "$1,844.3", &field_type, &field_meta); + } + NumberFormat::Yen => { + assert_equal(&type_option, "18443", "¥1,844.3", &field_type, &field_meta); + } + NumberFormat::EUR => { + assert_equal(&type_option, "18443", "€1.844,3", &field_type, &field_meta); + } + _ => {} + } + } + } + + #[test] + fn number_description_sign_test() { + let mut type_option = NumberTypeOption { + sign_positive: false, + ..Default::default() + }; + let field_type = FieldType::Number; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + + for format in NumberFormat::iter() { + type_option.format = format; + match format { + NumberFormat::Number => { + assert_equal(&type_option, "18443", "18443", &field_type, &field_meta); + } + NumberFormat::USD => { + assert_equal(&type_option, "18443", "-$18,443", &field_type, &field_meta); + } + NumberFormat::Yen => { + assert_equal(&type_option, "18443", "-¥18,443", &field_type, &field_meta); + } + NumberFormat::EUR => { + assert_equal(&type_option, "18443", "-€18.443", &field_type, &field_meta); + } + _ => {} + } + } + } + + fn assert_equal( + type_option: &NumberTypeOption, + cell_data: &str, + expected_str: &str, + field_type: &FieldType, + field_meta: &FieldMeta, + ) { + assert_eq!( + type_option + .decode_cell_data(data(cell_data), field_type, field_meta) + .unwrap() + .content, + expected_str.to_owned() + ); + } + + fn data(s: &str) -> String { + s.to_owned() + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs index 3d9cee701f..c73dd1802f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs @@ -95,26 +95,30 @@ impl SelectOptionOperation for SingleSelectTypeOption { } } -impl CellDataOperation for SingleSelectTypeOption { - fn decode_cell_data>( +impl CellDataOperation for SingleSelectTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, _field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into, + { if !decoded_field_type.is_select_option() { - return DecodedCellData::default(); + return Ok(DecodedCellData::default()); } - if let Some(option_id) = select_option_ids(type_option_cell_data.data).first() { - return match self.options.iter().find(|option| &option.id == option_id) { + let cell_data = encoded_data.into(); + if let Some(option_id) = select_option_ids(cell_data).first() { + let data = match self.options.iter().find(|option| &option.id == option_id) { None => DecodedCellData::default(), Some(option) => DecodedCellData::from_content(option.name.clone()), }; + Ok(data) + } else { + Ok(DecodedCellData::default()) } - - DecodedCellData::default() } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -187,19 +191,21 @@ impl SelectOptionOperation for MultiSelectTypeOption { } } -impl CellDataOperation for MultiSelectTypeOption { - fn decode_cell_data>( +impl CellDataOperation for MultiSelectTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, _field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into, + { if !decoded_field_type.is_select_option() { - return DecodedCellData::default(); + return Ok(DecodedCellData::default()); } - - let option_ids = select_option_ids(type_option_cell_data.data); + let cell_data = encoded_data.into(); + let option_ids = select_option_ids(cell_data); let content = self .options .iter() @@ -208,7 +214,7 @@ impl CellDataOperation for MultiSelectTypeOption { .collect::>() .join(SELECTION_IDS_SEPARATOR); - DecodedCellData::from_content(content) + Ok(DecodedCellData::from_content(content)) } fn apply_changeset(&self, changeset: T, cell_meta: Option) -> Result @@ -491,108 +497,144 @@ fn make_select_context_from(cell_meta: &Option, options: &[SelectOptio } } -// #[cfg(test)] -// mod tests { -// use crate::services::field::FieldBuilder; -// use crate::services::field::{ -// MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset, -// SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, -// }; -// use crate::services::row::CellDataOperation; -// -// #[test] -// fn single_select_test() { -// let google_option = SelectOption::new("Google"); -// let facebook_option = SelectOption::new("Facebook"); -// let twitter_option = SelectOption::new("Twitter"); -// let single_select = SingleSelectTypeOptionBuilder::default() -// .option(google_option.clone()) -// .option(facebook_option.clone()) -// .option(twitter_option); -// -// let field_meta = FieldBuilder::new(single_select) -// .name("Platform") -// .visibility(true) -// .build(); -// -// let type_option = SingleSelectTypeOption::from(&field_meta); -// -// let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR); -// let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); -// let cell_data = type_option.apply_changeset(data, None).unwrap(); -// assert_eq!( -// type_option.decode_cell_data(cell_data, &field_meta).content, -// google_option.name, -// ); -// -// let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); -// let cell_data = type_option.apply_changeset(data, None).unwrap(); -// assert_eq!( -// type_option.decode_cell_data(cell_data, &field_meta).content, -// google_option.name, -// ); -// -// // Invalid option id -// let cell_data = type_option -// .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) -// .unwrap(); -// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); -// -// // Invalid option id -// let cell_data = type_option -// .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None) -// .unwrap(); -// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); -// -// // Invalid changeset -// assert!(type_option.apply_changeset("123", None).is_err()); -// } -// -// #[test] -// fn multi_select_test() { -// let google_option = SelectOption::new("Google"); -// let facebook_option = SelectOption::new("Facebook"); -// let twitter_option = SelectOption::new("Twitter"); -// let multi_select = MultiSelectTypeOptionBuilder::default() -// .option(google_option.clone()) -// .option(facebook_option.clone()) -// .option(twitter_option); -// -// let field_meta = FieldBuilder::new(multi_select) -// .name("Platform") -// .visibility(true) -// .build(); -// -// let type_option = MultiSelectTypeOption::from(&field_meta); -// -// let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); -// let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); -// let cell_data = type_option.apply_changeset(data, None).unwrap(); -// assert_eq!( -// type_option.decode_cell_data(cell_data, &field_meta).content, -// vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR), -// ); -// -// let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); -// let cell_data = type_option.apply_changeset(data, None).unwrap(); -// assert_eq!( -// type_option.decode_cell_data(cell_data, &field_meta).content, -// google_option.name, -// ); -// -// // Invalid option id -// let cell_data = type_option -// .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) -// .unwrap(); -// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); -// -// // Invalid option id -// let cell_data = type_option -// .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None) -// .unwrap(); -// assert_eq!(type_option.decode_cell_data(cell_data, &field_meta).content, "",); -// -// // Invalid changeset -// assert!(type_option.apply_changeset("123", None).is_err()); -// } -// } +#[cfg(test)] +mod tests { + use crate::services::field::FieldBuilder; + use crate::services::field::{ + MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset, + SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, + }; + use crate::services::row::CellDataOperation; + + #[test] + fn single_select_test() { + let google_option = SelectOption::new("Google"); + let facebook_option = SelectOption::new("Facebook"); + let twitter_option = SelectOption::new("Twitter"); + let single_select = SingleSelectTypeOptionBuilder::default() + .option(google_option.clone()) + .option(facebook_option.clone()) + .option(twitter_option); + + let field_meta = FieldBuilder::new(single_select) + .name("Platform") + .visibility(true) + .build(); + + let type_option = SingleSelectTypeOption::from(&field_meta); + + let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR); + let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); + let cell_data = type_option.apply_changeset(data, None).unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + google_option.name, + ); + + let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); + let cell_data = type_option.apply_changeset(data, None).unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + google_option.name, + ); + + // Invalid option id + let cell_data = type_option + .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) + .unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + "", + ); + + // Invalid option id + let cell_data = type_option + .apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None) + .unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + "", + ); + + // Invalid changeset + assert!(type_option.apply_changeset("123", None).is_err()); + } + + #[test] + fn multi_select_test() { + let google_option = SelectOption::new("Google"); + let facebook_option = SelectOption::new("Facebook"); + let twitter_option = SelectOption::new("Twitter"); + let multi_select = MultiSelectTypeOptionBuilder::default() + .option(google_option.clone()) + .option(facebook_option.clone()) + .option(twitter_option); + + let field_meta = FieldBuilder::new(multi_select) + .name("Platform") + .visibility(true) + .build(); + + let type_option = MultiSelectTypeOption::from(&field_meta); + + let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); + let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str(); + let cell_data = type_option.apply_changeset(data, None).unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR), + ); + + let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); + let cell_data = type_option.apply_changeset(data, None).unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + google_option.name, + ); + + // Invalid option id + let cell_data = type_option + .apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None) + .unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + "", + ); + + // Invalid option id + let cell_data = type_option + .apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None) + .unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .content, + "", + ); + + // Invalid changeset + assert!(type_option.apply_changeset("123", None).is_err()); + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index 7cf4fc3d3e..32d45a3e29 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs @@ -1,16 +1,13 @@ use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{ - decode_cell_data, CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData, -}; +use crate::services::row::{decode_cell_data, CellContentChangeset, CellDataOperation, DecodedCellData}; use bytes::Bytes; use flowy_derive::ProtoBuf; -use flowy_error::FlowyError; +use flowy_error::{FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; use serde::{Deserialize, Serialize}; -use std::str::FromStr; #[derive(Default)] pub struct RichTextTypeOptionBuilder(RichTextTypeOption); @@ -34,23 +31,25 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); -impl CellDataOperation for RichTextTypeOption { - fn decode_cell_data>( +impl CellDataOperation for RichTextTypeOption { + fn decode_cell_data( &self, - type_option_cell_data: T, + encoded_data: T, decoded_field_type: &FieldType, field_meta: &FieldMeta, - ) -> DecodedCellData { - let type_option_cell_data = type_option_cell_data.into(); + ) -> FlowyResult + where + T: Into, + { if decoded_field_type.is_date() || decoded_field_type.is_single_select() || decoded_field_type.is_multi_select() || decoded_field_type.is_number() { - let field_type = type_option_cell_data.field_type.clone(); - decode_cell_data(type_option_cell_data, field_meta, &field_type).unwrap_or_default() + decode_cell_data(encoded_data, decoded_field_type, decoded_field_type, field_meta) } else { - DecodedCellData::from_content(type_option_cell_data.data) + let cell_data = encoded_data.into(); + Ok(DecodedCellData::from_content(cell_data)) } } @@ -67,64 +66,71 @@ impl CellDataOperation for RichTextTypeOption { } } -// #[cfg(test)] -// mod tests { -// use crate::services::field::FieldBuilder; -// use crate::services::field::*; -// use crate::services::row::{CellDataOperation, TypeOptionCellData}; -// use flowy_grid_data_model::entities::FieldType; -// -// #[test] -// fn text_description_test() { -// let type_option = RichTextTypeOption::default(); -// -// // date -// let date_time_field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); -// let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); -// let data = TypeOptionCellData::new(&json, FieldType::DateTime).json(); -// assert_eq!( -// type_option.decode_cell_data(data, &date_time_field_meta).content, -// "Mar 14,2022".to_owned() -// ); -// -// // Single select -// let done_option = SelectOption::new("Done"); -// let done_option_id = done_option.id.clone(); -// let single_select = SingleSelectTypeOptionBuilder::default().option(done_option); -// let single_select_field_meta = FieldBuilder::new(single_select).build(); -// let cell_data = TypeOptionCellData::new(&done_option_id, FieldType::SingleSelect).json(); -// assert_eq!( -// type_option -// .decode_cell_data(cell_data, &single_select_field_meta) -// .content, -// "Done".to_owned() -// ); -// -// // Multiple select -// let google_option = SelectOption::new("Google"); -// let facebook_option = SelectOption::new("Facebook"); -// let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); -// let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str(); -// let multi_select = MultiSelectTypeOptionBuilder::default() -// .option(google_option) -// .option(facebook_option); -// let multi_select_field_meta = FieldBuilder::new(multi_select).build(); -// let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta); -// let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap(); -// assert_eq!( -// type_option -// .decode_cell_data(cell_data, &multi_select_field_meta) -// .content, -// "Google,Facebook".to_owned() -// ); -// -// //Number -// let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); -// let number_field_meta = FieldBuilder::new(number).build(); -// let data = TypeOptionCellData::new("18443", FieldType::Number).json(); -// assert_eq!( -// type_option.decode_cell_data(data, &number_field_meta).content, -// "$18,443".to_owned() -// ); -// } -// } +#[cfg(test)] +mod tests { + use crate::services::field::FieldBuilder; + use crate::services::field::*; + use crate::services::row::CellDataOperation; + use flowy_grid_data_model::entities::FieldType; + + #[test] + fn text_description_test() { + let type_option = RichTextTypeOption::default(); + + // date + let field_type = FieldType::DateTime; + let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); + let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); + assert_eq!( + type_option + .decode_cell_data(json, &field_type, &date_time_field_meta) + .unwrap() + .content, + "Mar 14,2022".to_owned() + ); + + // Single select + let done_option = SelectOption::new("Done"); + let done_option_id = done_option.id.clone(); + let single_select = SingleSelectTypeOptionBuilder::default().option(done_option); + let single_select_field_meta = FieldBuilder::new(single_select).build(); + + assert_eq!( + type_option + .decode_cell_data(done_option_id, &FieldType::SingleSelect, &single_select_field_meta) + .unwrap() + .content, + "Done".to_owned() + ); + + // Multiple select + let google_option = SelectOption::new("Google"); + let facebook_option = SelectOption::new("Facebook"); + let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR); + let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str(); + let multi_select = MultiSelectTypeOptionBuilder::default() + .option(google_option) + .option(facebook_option); + let multi_select_field_meta = FieldBuilder::new(multi_select).build(); + let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta); + let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap(); + assert_eq!( + type_option + .decode_cell_data(cell_data, &FieldType::MultiSelect, &multi_select_field_meta) + .unwrap() + .content, + "Google,Facebook".to_owned() + ); + + //Number + let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); + let number_field_meta = FieldBuilder::new(number).build(); + assert_eq!( + type_option + .decode_cell_data("18443".to_owned(), &FieldType::Number, &number_field_meta) + .unwrap() + .content, + "$18,443".to_owned() + ); + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs index 2bb78a0bf0..9911e4cab3 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs @@ -1,22 +1,26 @@ use crate::services::field::*; -use flowy_error::FlowyError; +use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType}; use serde::{Deserialize, Serialize}; use std::fmt::Formatter; use std::str::FromStr; pub trait CellDataOperation { - fn decode_cell_data>( + fn decode_cell_data( &self, - data: T, + encoded_data: T, decoded_field_type: &FieldType, field_meta: &FieldMeta, - ) -> DecodedCellData; + ) -> FlowyResult + where + T: Into; + + // fn apply_changeset>( &self, changeset: C, cell_meta: Option, - ) -> Result; + ) -> FlowyResult; } #[derive(Debug)] @@ -72,14 +76,6 @@ impl std::convert::TryInto for String { } } -// impl std::convert::Into for String { -// type Error = FlowyError; -// -// fn try_into(self) -> Result { -// TypeOptionCellData::from_str(&self) -// } -// } - impl TypeOptionCellData { pub fn new(data: T, field_type: FieldType) -> Self { TypeOptionCellData { @@ -142,40 +138,89 @@ pub fn apply_cell_data_changeset>( Ok(TypeOptionCellData::new(s, field_meta.field_type.clone()).json()) } -pub fn decode_cell_data>( +pub fn decode_cell_data_from_type_option_cell_data>( data: T, field_meta: &FieldMeta, field_type: &FieldType, -) -> Option { +) -> DecodedCellData { if let Ok(type_option_cell_data) = data.try_into() { - let (decoded_field_type, data) = &type_option_cell_data.split(); - let s = match field_type { - FieldType::RichText => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - FieldType::Number => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - FieldType::DateTime => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - FieldType::SingleSelect => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - FieldType::MultiSelect => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - FieldType::Checkbox => field_meta - .get_type_option_entry::(field_type)? - .decode_cell_data(type_option_cell_data, decoded_field_type, field_meta), - }; - tracing::Span::current().record( - "content", - &format!("{:?}: {}", field_meta.field_type, s.content).as_str(), - ); - Some(s) + let (encoded_data, s_field_type) = type_option_cell_data.split(); + decode_cell_data(encoded_data, &s_field_type, field_type, field_meta).unwrap_or_default() } else { - Some(DecodedCellData::default()) + DecodedCellData::default() + } +} + +pub fn decode_cell_data>( + encoded_data: T, + s_field_type: &FieldType, + t_field_type: &FieldType, + field_meta: &FieldMeta, +) -> FlowyResult { + let encoded_data = encoded_data.into(); + let get_cell_data = || { + let data = match t_field_type { + FieldType::RichText => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::Number => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::DateTime => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::SingleSelect => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::MultiSelect => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::Checkbox => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), + }; + Some(data) + }; + + match get_cell_data() { + Some(Ok(data)) => { + tracing::Span::current().record( + "content", + &format!("{:?}: {}", field_meta.field_type, data.content).as_str(), + ); + Ok(data) + } + Some(Err(err)) => { + tracing::error!("{:?}", err); + Ok(DecodedCellData::default()) + } + None => Ok(DecodedCellData::default()), + } +} + +pub(crate) struct EncodedCellData(pub Option); + +impl EncodedCellData { + pub fn try_into_inner(self) -> FlowyResult { + match self.0 { + None => Err(ErrorCode::InvalidData.into()), + Some(data) => Ok(data), + } + } +} + +impl std::convert::From for EncodedCellData +where + T: FromStr, +{ + fn from(s: String) -> Self { + match T::from_str(&s) { + Ok(inner) => EncodedCellData(Some(inner)), + Err(e) => { + tracing::error!("Deserialize Cell Data failed: {}", e); + EncodedCellData(None) + } + } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs index edcb102595..af4af0e491 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs @@ -1,4 +1,4 @@ -use crate::services::row::decode_cell_data; +use crate::services::row::decode_cell_data_from_type_option_cell_data; use flowy_error::FlowyResult; use flowy_grid_data_model::entities::{ Cell, CellMeta, FieldMeta, GridBlock, GridBlockOrder, RepeatedGridBlock, Row, RowMeta, RowOrder, @@ -31,14 +31,16 @@ pub fn make_cell_by_field_id( cell_meta: CellMeta, ) -> Option<(String, Cell)> { let field_meta = field_map.get(&field_id)?; - let (raw, content) = decode_cell_data(cell_meta.data, field_meta, &field_meta.field_type)?.split(); + let (raw, content) = + decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split(); let cell = Cell::new(&field_id, content, raw); Some((field_id, cell)) } pub fn make_cell(field_id: &str, field_meta: &FieldMeta, row_meta: &RowMeta) -> Option { let cell_meta = row_meta.cells.get(field_id)?.clone(); - let (raw, content) = decode_cell_data(cell_meta.data, field_meta, &field_meta.field_type)?.split(); + let (raw, content) = + decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split(); Some(Cell::new(field_id, content, raw)) } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs index a954694a16..f4549cb726 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs @@ -5,7 +5,7 @@ use flowy_grid::services::field::{ DateCellContentChangeset, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR, }; -use flowy_grid::services::row::{decode_cell_data, CreateRowMetaBuilder}; +use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder}; use flowy_grid_data_model::entities::{ CellChangeset, FieldChangesetParams, FieldType, GridBlockMeta, GridBlockMetaChangeset, RowMetaChangeset, TypeOptionDataEntry, @@ -291,8 +291,7 @@ async fn grid_row_add_date_cell_test() { let date_field = date_field.unwrap(); let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone(); assert_eq!( - decode_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type) - .unwrap() + decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type) .split() .1, "2022/03/16", diff --git a/shared-lib/flowy-grid-data-model/src/entities/grid.rs b/shared-lib/flowy-grid-data-model/src/entities/grid.rs index 5cde193623..e8f3d615c4 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -912,6 +912,34 @@ impl FieldType { _ => 150, } } + + pub fn is_number(&self) -> bool { + self == &FieldType::Number + } + + pub fn is_text(&self) -> bool { + self == &FieldType::RichText + } + + pub fn is_checkbox(&self) -> bool { + self == &FieldType::Checkbox + } + + pub fn is_date(&self) -> bool { + self == &FieldType::DateTime + } + + pub fn is_single_select(&self) -> bool { + self == &FieldType::SingleSelect + } + + pub fn is_multi_select(&self) -> bool { + self == &FieldType::MultiSelect + } + + pub fn is_select_option(&self) -> bool { + self == &FieldType::MultiSelect || self == &FieldType::SingleSelect + } } #[derive(Debug, Clone, Default, ProtoBuf)] diff --git a/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs b/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs index a65d4d5bd9..a44f0cb267 100644 --- a/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs +++ b/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs @@ -153,27 +153,22 @@ pub fn check_pb_dart_plugin() { let output = Command::new("sh").arg("-c").arg("echo $PATH").output(); let paths = String::from_utf8(output.unwrap().stdout) .unwrap() - .split(":") + .split(':') .map(|s| s.to_string()) .collect::>(); paths.iter().for_each(|s| msg.push_str(&format!("{}\n", s))); - match Command::new("sh").arg("-c").arg("which protoc-gen-dart").output() { - Ok(output) => { - msg.push_str(&format!( - "Installed protoc-gen-dart path: {:?}\n", - String::from_utf8(output.stdout).unwrap() - )); - } - Err(_) => {} + if let Ok(output) = Command::new("sh").arg("-c").arg("which protoc-gen-dart").output() { + msg.push_str(&format!( + "Installed protoc-gen-dart path: {:?}\n", + String::from_utf8(output.stdout).unwrap() + )); } - msg.push_str(&format!("✅ You can fix that by adding:")); - msg.push_str(&format!("\n\texport PATH=\"$PATH\":\"$HOME/.pub-cache/bin\"\n",)); - msg.push_str(&format!( - "to your shell's config file.(.bashrc, .bash, .profile, .zshrc etc.)" - )); + msg.push_str(&"✅ You can fix that by adding:".to_string()); + msg.push_str(&"\n\texport PATH=\"$PATH\":\"$HOME/.pub-cache/bin\"\n".to_string()); + msg.push_str(&"to your shell's config file.(.bashrc, .bash, .profile, .zshrc etc.)".to_string()); panic!("{}", msg) } }