From 467e0093b23ff4a9ba8ed2390dee529f915584c9 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 15 May 2022 17:13:01 +0800 Subject: [PATCH 01/82] refactor: rename files --- .../app_flowy/lib/startup/deps_resolver.dart | 4 +- ...bloc.dart => select_option_cell_bloc.dart} | 28 ++++---- ...oc.dart => select_option_editor_bloc.dart} | 6 +- .../workspace/application/grid/prelude.dart | 2 +- .../grid/src/widgets/cell/cell_builder.dart | 2 +- .../src/widgets/cell/date_cell/date_cell.dart | 4 +- .../{calendar.dart => date_editor.dart} | 21 ++---- .../grid/src/widgets/cell/prelude.dart | 2 +- .../extension.dart | 0 .../select_option_cell.dart} | 14 ++-- .../select_option_editor.dart} | 26 +++---- .../text_field.dart | 0 .../header/type_option/multi_select.dart | 10 +-- ..._option_pannel.dart => select_option.dart} | 70 ++++++++++--------- ..._pannel.dart => select_option_editor.dart} | 6 +- .../header/type_option/single_select.dart | 10 +-- 16 files changed, 101 insertions(+), 104 deletions(-) rename frontend/app_flowy/lib/workspace/application/grid/cell/{selection_cell_bloc.dart => select_option_cell_bloc.dart} (69%) rename frontend/app_flowy/lib/workspace/application/grid/cell/{selection_editor_bloc.dart => select_option_editor_bloc.dart} (97%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/{calendar.dart => date_editor.dart} (95%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/{selection_cell => select_option_cell}/extension.dart (100%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/{selection_cell/selection_cell.dart => select_option_cell/select_option_cell.dart} (90%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/{selection_cell/selection_editor.dart => select_option_cell/select_option_editor.dart} (89%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/{selection_cell => select_option_cell}/text_field.dart (100%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/{field_option_pannel.dart => select_option.dart} (82%) rename frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/{edit_option_pannel.dart => select_option_editor.dart} (97%) diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 8bc5d4dd41..1f7f3524fa 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -170,8 +170,8 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( - (context, _) => SelectionCellBloc( + getIt.registerFactoryParam( + (context, _) => SelectOptionCellBloc( cellContext: context, ), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart similarity index 69% rename from frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart rename to frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart index 794ccd3002..056b65e556 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart @@ -4,16 +4,16 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; -part 'selection_cell_bloc.freezed.dart'; +part 'select_option_cell_bloc.freezed.dart'; -class SelectionCellBloc extends Bloc { +class SelectOptionCellBloc extends Bloc { final GridSelectOptionCellContext cellContext; void Function()? _onCellChangedFn; - SelectionCellBloc({ + SelectOptionCellBloc({ required this.cellContext, - }) : super(SelectionCellState.initial(cellContext)) { - on( + }) : super(SelectOptionCellState.initial(cellContext)) { + on( (event, emit) async { await event.map( initial: (_InitialCell value) async { @@ -44,7 +44,7 @@ class SelectionCellBloc extends Bloc { _onCellChangedFn = cellContext.startListening( onCellChanged: ((selectOptionContext) { if (!isClosed) { - add(SelectionCellEvent.didReceiveOptions( + add(SelectOptionCellEvent.didReceiveOptions( selectOptionContext.options, selectOptionContext.selectOptions, )); @@ -55,25 +55,25 @@ class SelectionCellBloc extends Bloc { } @freezed -class SelectionCellEvent with _$SelectionCellEvent { - const factory SelectionCellEvent.initial() = _InitialCell; - const factory SelectionCellEvent.didReceiveOptions( +class SelectOptionCellEvent with _$SelectOptionCellEvent { + const factory SelectOptionCellEvent.initial() = _InitialCell; + const factory SelectOptionCellEvent.didReceiveOptions( List options, List selectedOptions, ) = _DidReceiveOptions; } @freezed -class SelectionCellState with _$SelectionCellState { - const factory SelectionCellState({ +class SelectOptionCellState with _$SelectOptionCellState { + const factory SelectOptionCellState({ required List options, required List selectedOptions, - }) = _SelectionCellState; + }) = _SelectOptionCellState; - factory SelectionCellState.initial(GridSelectOptionCellContext context) { + factory SelectOptionCellState.initial(GridSelectOptionCellContext context) { final data = context.getCellData(); - return SelectionCellState( + return SelectOptionCellState( options: data?.options ?? [], selectedOptions: data?.selectOptions ?? [], ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart similarity index 97% rename from frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart rename to frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart index cc7af58531..2702e6006f 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart @@ -7,14 +7,14 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; import 'select_option_service.dart'; -part 'selection_editor_bloc.freezed.dart'; +part 'select_option_editor_bloc.freezed.dart'; -class SelectOptionEditorBloc extends Bloc { +class SelectOptionCellEditorBloc extends Bloc { final SelectOptionService _selectOptionService; final GridSelectOptionCellContext cellContext; void Function()? _onCellChangedFn; - SelectOptionEditorBloc({ + SelectOptionCellEditorBloc({ required this.cellContext, }) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell), super(SelectOptionEditorState.initial(cellContext)) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart index e06f5d9da8..3a0ebc5112 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart @@ -18,7 +18,7 @@ export 'field/type_option/single_select_bloc.dart'; // Cell export 'cell/text_cell_bloc.dart'; export 'cell/number_cell_bloc.dart'; -export 'cell/selection_cell_bloc.dart'; +export 'cell/select_option_cell_bloc.dart'; export 'cell/date_cell_bloc.dart'; export 'cell/checkbox_cell_bloc.dart'; export 'cell/cell_service/cell_service.dart'; 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 7d21cb7e9a..eab2903857 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 @@ -11,7 +11,7 @@ import 'package:styled_widget/styled_widget.dart'; import 'checkbox_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; -import 'selection_cell/selection_cell.dart'; +import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) { 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 d1d15ea4b3..5f0ccbc834 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 @@ -5,7 +5,7 @@ import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import '../cell_builder.dart'; -import 'calendar.dart'; +import 'date_editor.dart'; class DateCellStyle extends GridCellStyle { Alignment alignment; @@ -77,7 +77,7 @@ class _DateCellState extends State { void _showCalendar(BuildContext context) { final bloc = context.read(); widget.onFocus.value = true; - final calendar = CellCalendar(onDismissed: () => widget.onFocus.value = false); + final calendar = DateCellEditor(onDismissed: () => widget.onFocus.value = false); calendar.show( context, cellContext: bloc.cellContext.clone(), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart similarity index 95% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart index c19e8c3001..abf8288c45 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart @@ -22,10 +22,10 @@ final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); const kMargin = EdgeInsets.symmetric(horizontal: 6, vertical: 10); -class CellCalendar with FlowyOverlayDelegate { +class DateCellEditor with FlowyOverlayDelegate { final VoidCallback onDismissed; - const CellCalendar({ + const DateCellEditor({ required this.onDismissed, }); @@ -33,23 +33,14 @@ class CellCalendar with FlowyOverlayDelegate { BuildContext context, { required GridDateCellContext cellContext, }) async { - CellCalendar.remove(context); + DateCellEditor.remove(context); final result = await cellContext.getTypeOptionData(); result.fold( (data) { - final typeOptionData = DateTypeOption.fromBuffer(data); - // DateTime? selectedDay; - // final cellData = cellContext.getCellData(); - - // if (cellData != null) { - // final timestamp = $fixnum.Int64.parseInt(cellData).toInt(); - // selectedDay = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); - // } - final calendar = _CellCalendarWidget( cellContext: cellContext, - dateTypeOption: typeOptionData, + dateTypeOption: DateTypeOption.fromBuffer(data), ); FlowyOverlay.of(context).insertWithAnchor( @@ -57,7 +48,7 @@ class CellCalendar with FlowyOverlayDelegate { child: calendar, constraints: BoxConstraints.loose(const Size(320, 500)), ), - identifier: CellCalendar.identifier(), + identifier: DateCellEditor.identifier(), anchorContext: context, anchorDirection: AnchorDirection.leftWithCenterAligned, style: FlowyOverlayStyle(blur: false), @@ -73,7 +64,7 @@ class CellCalendar with FlowyOverlayDelegate { } static String identifier() { - return (CellCalendar).toString(); + return (DateCellEditor).toString(); } @override diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart index 3c7e43ecc0..792e58810f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart @@ -3,4 +3,4 @@ export 'text_cell.dart'; export 'number_cell.dart'; export 'date_cell/date_cell.dart'; export 'checkbox_cell.dart'; -export 'selection_cell/selection_cell.dart'; +export 'select_option_cell/select_option_cell.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/extension.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart similarity index 100% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/extension.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart similarity index 90% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart index 0c754b0297..12415c816d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart @@ -10,7 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'extension.dart'; -import 'selection_editor.dart'; +import 'select_option_editor.dart'; class SelectOptionCellStyle extends GridCellStyle { String placeholder; @@ -41,12 +41,12 @@ class SingleSelectCell extends GridCellWidget { } class _SingleSelectCellState extends State { - late SelectionCellBloc _cellBloc; + late SelectOptionCellBloc _cellBloc; @override void initState() { final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext; - _cellBloc = getIt(param1: cellContext)..add(const SelectionCellEvent.initial()); + _cellBloc = getIt(param1: cellContext)..add(const SelectOptionCellEvent.initial()); super.initState(); } @@ -54,7 +54,7 @@ class _SingleSelectCellState extends State { Widget build(BuildContext context) { return BlocProvider.value( value: _cellBloc, - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { return _SelectOptionCell( selectOptions: state.selectedOptions, @@ -95,12 +95,12 @@ class MultiSelectCell extends GridCellWidget { } class _MultiSelectCellState extends State { - late SelectionCellBloc _cellBloc; + late SelectOptionCellBloc _cellBloc; @override void initState() { final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext; - _cellBloc = getIt(param1: cellContext)..add(const SelectionCellEvent.initial()); + _cellBloc = getIt(param1: cellContext)..add(const SelectOptionCellEvent.initial()); super.initState(); } @@ -108,7 +108,7 @@ class _MultiSelectCellState extends State { Widget build(BuildContext context) { return BlocProvider.value( value: _cellBloc, - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { return _SelectOptionCell( selectOptions: state.selectedOptions, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart similarity index 89% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart index 6e96fccbfb..2effe120c2 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart @@ -1,8 +1,8 @@ import 'dart:collection'; import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; -import 'package:app_flowy/workspace/application/grid/cell/selection_editor_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/cell/select_option_editor_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -37,10 +37,10 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SelectOptionEditorBloc( + create: (context) => SelectOptionCellEditorBloc( cellContext: cellContext, )..add(const SelectOptionEditorEvent.initial()), - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { return CustomScrollView( shrinkWrap: true, @@ -102,7 +102,7 @@ class _OptionList extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { List cells = []; cells.addAll(state.options.map((option) { @@ -145,7 +145,7 @@ class _TextField extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { final optionMap = LinkedHashMap.fromIterable(state.selectedOptions, key: (option) => option.name, value: (option) => option); @@ -158,10 +158,10 @@ class _TextField extends StatelessWidget { distanceToText: _editorPannelWidth * 0.7, tagController: _tagController, newText: (text) { - context.read().add(SelectOptionEditorEvent.filterOption(text)); + context.read().add(SelectOptionEditorEvent.filterOption(text)); }, onNewTag: (tagName) { - context.read().add(SelectOptionEditorEvent.newOption(tagName)); + context.read().add(SelectOptionEditorEvent.newOption(tagName)); }, ), ); @@ -230,7 +230,7 @@ class _SelectOptionCell extends StatelessWidget { _body(theme, context), InkWell( onTap: () { - context.read().add(SelectOptionEditorEvent.selectOption(option.id)); + context.read().add(SelectOptionEditorEvent.selectOption(option.id)); }, ), ], @@ -270,17 +270,17 @@ class _SelectOptionCell extends StatelessWidget { } void _showEditPannel(BuildContext context) { - final pannel = EditSelectOptionPannel( + final pannel = SelectOptionTypeOptionEditor( option: option, onDeleted: () { - context.read().add(SelectOptionEditorEvent.deleteOption(option)); + context.read().add(SelectOptionEditorEvent.deleteOption(option)); }, onUpdated: (updatedOption) { - context.read().add(SelectOptionEditorEvent.updateOption(updatedOption)); + context.read().add(SelectOptionEditorEvent.updateOption(updatedOption)); }, key: ValueKey(option.id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value. ); - final overlayIdentifier = (EditSelectOptionPannel).toString(); + final overlayIdentifier = (SelectOptionTypeOptionEditor).toString(); FlowyOverlay.of(context).remove(overlayIdentifier); FlowyOverlay.of(context).insertWithAnchor( diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/text_field.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart similarity index 100% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/text_field.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart index b5b3ece1a8..397c5e054d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart @@ -4,7 +4,7 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'field_option_pannel.dart'; +import 'select_option.dart'; class MultiSelectTypeOptionBuilder extends TypeOptionBuilder { final MultiSelectTypeOptionWidget _widget; @@ -43,18 +43,18 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()); }, builder: (context, state) { - return FieldSelectOptionPannel( + return SelectOptionTypeOptionWidget( options: state.typeOption.options, beginEdit: () { overlayDelegate.hideOverlay(context); }, - createOptionCallback: (name) { + createSelectOptionCallback: (name) { context.read().add(MultiSelectTypeOptionEvent.createOption(name)); }, - updateOptionCallback: (updateOption) { + updateSelectOptionCallback: (updateOption) { context.read().add(MultiSelectTypeOptionEvent.updateOption(updateOption)); }, - deleteOptionCallback: (deleteOption) { + deleteSelectOptionCallback: (deleteOption) { context.read().add(MultiSelectTypeOptionEvent.deleteOption(deleteOption)); }, overlayDelegate: overlayDelegate, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/field_option_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart similarity index 82% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/field_option_pannel.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart index ce6c69d2cd..c00a473b38 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/field_option_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart @@ -13,22 +13,22 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; -import 'edit_option_pannel.dart'; +import 'select_option_editor.dart'; -class FieldSelectOptionPannel extends StatelessWidget { +class SelectOptionTypeOptionWidget extends StatelessWidget { final List options; final VoidCallback beginEdit; - final Function(String optionName) createOptionCallback; - final Function(SelectOption) updateOptionCallback; - final Function(SelectOption) deleteOptionCallback; + final Function(String optionName) createSelectOptionCallback; + final Function(SelectOption) updateSelectOptionCallback; + final Function(SelectOption) deleteSelectOptionCallback; final TypeOptionOverlayDelegate overlayDelegate; - const FieldSelectOptionPannel({ + const SelectOptionTypeOptionWidget({ required this.options, required this.beginEdit, - required this.createOptionCallback, - required this.updateOptionCallback, - required this.deleteOptionCallback, + required this.createSelectOptionCallback, + required this.updateSelectOptionCallback, + required this.deleteSelectOptionCallback, required this.overlayDelegate, Key? key, }) : super(key: key); @@ -44,17 +44,17 @@ class FieldSelectOptionPannel extends StatelessWidget { } state.newOptionName.fold( () => null, - (optionName) => createOptionCallback(optionName), + (optionName) => createSelectOptionCallback(optionName), ); state.updateOption.fold( () => null, - (updateOption) => updateOptionCallback(updateOption), + (updateOption) => updateSelectOptionCallback(updateOption), ); state.deleteOption.fold( () => null, - (deleteOption) => deleteOptionCallback(deleteOption), + (deleteOption) => deleteSelectOptionCallback(deleteOption), ); }, builder: (context, state) { @@ -86,30 +86,12 @@ class OptionTitle extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = context.watch(); - return BlocBuilder( builder: (context, state) { List children = [FlowyText.medium(LocaleKeys.grid_field_optionTitle.tr(), fontSize: 12)]; if (state.options.isNotEmpty) { children.add(const Spacer()); - children.add( - SizedBox( - width: 100, - height: 26, - child: FlowyButton( - text: FlowyText.medium( - LocaleKeys.grid_field_addOption.tr(), - fontSize: 12, - textAlign: TextAlign.center, - ), - hoverColor: theme.hover, - onTap: () { - context.read().add(const FieldOptionPannelEvent.beginAddingOption()); - }, - ), - ), - ); + children.add(const _OptionTitleAddOptionButton()); } return SizedBox( @@ -121,6 +103,30 @@ class OptionTitle extends StatelessWidget { } } +class _OptionTitleAddOptionButton extends StatelessWidget { + const _OptionTitleAddOptionButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return SizedBox( + width: 100, + height: 26, + child: FlowyButton( + text: FlowyText.medium( + LocaleKeys.grid_field_addOption.tr(), + fontSize: 12, + textAlign: TextAlign.center, + ), + hoverColor: theme.hover, + onTap: () { + context.read().add(const FieldOptionPannelEvent.beginAddingOption()); + }, + ), + ); + } +} + class _OptionList extends StatelessWidget { final TypeOptionOverlayDelegate delegate; const _OptionList(this.delegate, {Key? key}) : super(key: key); @@ -155,7 +161,7 @@ class _OptionList extends StatelessWidget { return _OptionCell( option: option, onEdited: (option) { - final pannel = EditSelectOptionPannel( + final pannel = SelectOptionTypeOptionEditor( option: option, onDeleted: () { delegate.hideOverlay(context); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart similarity index 97% rename from frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart rename to frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart index 62c4eca7e5..dada59b41c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart @@ -1,6 +1,6 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/edit_select_option_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/extension.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -14,11 +14,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; -class EditSelectOptionPannel extends StatelessWidget { +class SelectOptionTypeOptionEditor extends StatelessWidget { final SelectOption option; final VoidCallback onDeleted; final Function(SelectOption) onUpdated; - const EditSelectOptionPannel({ + const SelectOptionTypeOptionEditor({ required this.option, required this.onDeleted, required this.onUpdated, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart index f8cc2293f6..3b84eec495 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart @@ -3,7 +3,7 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/type_opti import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'field_option_pannel.dart'; +import 'select_option.dart'; class SingleSelectTypeOptionBuilder extends TypeOptionBuilder { final SingleSelectTypeOptionWidget _widget; @@ -42,18 +42,18 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget { dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()); }, builder: (context, state) { - return FieldSelectOptionPannel( + return SelectOptionTypeOptionWidget( options: state.typeOption.options, beginEdit: () { overlayDelegate.hideOverlay(context); }, - createOptionCallback: (name) { + createSelectOptionCallback: (name) { context.read().add(SingleSelectTypeOptionEvent.createOption(name)); }, - updateOptionCallback: (updateOption) { + updateSelectOptionCallback: (updateOption) { context.read().add(SingleSelectTypeOptionEvent.updateOption(updateOption)); }, - deleteOptionCallback: (deleteOption) { + deleteSelectOptionCallback: (deleteOption) { context.read().add(SingleSelectTypeOptionEvent.deleteOption(deleteOption)); }, overlayDelegate: overlayDelegate, From a59a0af05b37ecee0dc50821f91758f780a8165b Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 17 May 2022 16:57:37 +0800 Subject: [PATCH 02/82] refactor: remove FieldTypeOptionContext --- .../app_flowy/lib/startup/deps_resolver.dart | 6 +- .../cell/cell_service/context_builder.dart | 4 +- .../grid/field/field_action_sheet_bloc.dart | 10 +- .../grid/field/field_cell_bloc.dart | 12 +- .../grid/field/field_editor_bloc.dart | 47 +++--- .../grid/field/field_editor_pannel_bloc.dart | 10 +- .../application/grid/field/field_service.dart | 41 ++--- ...rt => select_option_type_option_bloc.dart} | 33 ++-- .../type_option/type_option_service.dart | 48 +++++- .../widgets/cell/date_cell/date_editor.dart | 2 +- .../grid/src/widgets/header/field_cell.dart | 8 +- .../grid/src/widgets/header/field_editor.dart | 27 ++-- .../widgets/header/field_editor_pannel.dart | 14 +- .../grid/src/widgets/header/grid_header.dart | 2 +- .../header/type_option/multi_select.dart | 2 +- .../header/type_option/select_option.dart | 54 +++---- .../header/type_option/single_select.dart | 2 +- .../grid/src/widgets/row/row_detail.dart | 2 +- .../src/widgets/toolbar/grid_property.dart | 2 +- .../dart_event/flowy-grid/dart_event.dart | 21 +-- .../flowy-grid-data-model/grid.pb.dart | 68 ++++---- .../flowy-grid-data-model/grid.pbjson.dart | 17 +- .../protobuf/flowy-grid/event_map.pbenum.dart | 6 +- .../protobuf/flowy-grid/event_map.pbjson.dart | 7 +- .../rust-lib/flowy-grid/src/event_handler.rs | 35 ++--- frontend/rust-lib/flowy-grid/src/event_map.rs | 10 +- .../src/protobuf/model/event_map.rs | 29 ++-- .../src/protobuf/proto/event_map.proto | 5 +- .../src/entities/grid.rs | 7 +- .../src/protobuf/model/grid.rs | 145 ++++++++++++------ .../src/protobuf/proto/grid.proto | 7 +- shared-lib/lib-infra/Cargo.toml | 1 + .../src/code_gen/protobuf_file/mod.rs | 37 ++++- 33 files changed, 401 insertions(+), 320 deletions(-) rename frontend/app_flowy/lib/workspace/application/grid/field/type_option/{field_option_pannel_bloc.dart => select_option_type_option_bloc.dart} (52%) diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 1f7f3524fa..7e65130fa8 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -15,7 +15,7 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/menu/menu.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show EditFieldContext; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldTypeOptionData; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; @@ -157,7 +157,7 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( + getIt.registerFactoryParam( (gridId, fieldLoader) => FieldEditorBloc( gridId: gridId, fieldLoader: fieldLoader, @@ -195,7 +195,7 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( + getIt.registerFactoryParam( (context, _) => FieldEditorPannelBloc(context), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index c7faff5311..4f81e119fc 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -150,8 +150,8 @@ class _GridCellContext extends Equatable { return data; } - Future, FlowyError>> getTypeOptionData() { - return _fieldService.getTypeOptionData(fieldType: fieldType); + Future> getTypeOptionData() { + return _fieldService.getFieldTypeOptionData(fieldType: fieldType); } Future> saveCellData(D data) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart index 6513fe85c3..25c9cbfc0d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart @@ -11,7 +11,7 @@ class FieldActionSheetBloc extends Bloc( (event, emit) async { await event.map( @@ -67,14 +67,14 @@ class FieldActionSheetEvent with _$FieldActionSheetEvent { @freezed class FieldActionSheetState with _$FieldActionSheetState { const factory FieldActionSheetState({ - required EditFieldContext editContext, + required FieldTypeOptionData fieldTypeOptionData, required String errorText, required String fieldName, }) = _FieldActionSheetState; - factory FieldActionSheetState.initial(EditFieldContext editContext) => FieldActionSheetState( - editContext: editContext, + factory FieldActionSheetState.initial(FieldTypeOptionData data) => FieldActionSheetState( + fieldTypeOptionData: data, errorText: '', - fieldName: editContext.gridField.name, + fieldName: data.field_2.name, ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart index f5fc41e57f..06a95ae89b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart @@ -19,16 +19,16 @@ class FieldCellBloc extends Bloc { super(FieldCellState.initial(cellContext)) { on( (event, emit) async { - await event.map( - initial: (_InitialCell value) async { + event.when( + initial: () { _startListening(); }, - didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { - emit(state.copyWith(field: value.field)); + didReceiveFieldUpdate: (field) { + emit(state.copyWith(field: field)); }, - updateWidth: (_UpdateWidth value) { + updateWidth: (offset) { final defaultWidth = state.field.width.toDouble(); - final width = defaultWidth + value.offset; + final width = defaultWidth + offset; if (width > defaultWidth && width < 300) { _fieldService.updateField(width: width); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart index d52fb8c312..db84425f86 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart @@ -12,27 +12,26 @@ part 'field_editor_bloc.freezed.dart'; class FieldEditorBloc extends Bloc { final String gridId; - final EditFieldContextLoader _loader; + final FieldContextLoader _loader; FieldEditorBloc({ required this.gridId, - required EditFieldContextLoader fieldLoader, + required FieldContextLoader fieldLoader, }) : _loader = fieldLoader, super(FieldEditorState.initial(gridId)) { on( (event, emit) async { await event.map( initial: (_InitialField value) async { - await _getEditFieldContext(emit); + await _getFieldTypeOptionContext(emit); }, updateName: (_UpdateName value) { final newContext = _updateEditContext(name: value.name); - emit(state.copyWith(editFieldContext: newContext)); + emit(state.copyWith(fieldTypeOptionData: newContext)); }, updateField: (_UpdateField value) { - final newContext = _updateEditContext(field: value.field, typeOptionData: value.typeOptionData); - - emit(state.copyWith(editFieldContext: newContext)); + final data = _updateEditContext(field: value.field, typeOptionData: value.typeOptionData); + emit(state.copyWith(fieldTypeOptionData: data)); }, done: (_Done value) async { await _saveField(emit); @@ -47,26 +46,26 @@ class FieldEditorBloc extends Bloc { return super.close(); } - Option _updateEditContext({ + Option _updateEditContext({ String? name, Field? field, List? typeOptionData, }) { - return state.editFieldContext.fold( + return state.fieldTypeOptionData.fold( () => none(), (context) { context.freeze(); - final newContext = context.rebuild((newContext) { - newContext.gridField.rebuild((newField) { + final newFieldTypeOptionData = context.rebuild((newContext) { + newContext.field_2.rebuild((newField) { if (name != null) { newField.name = name; } - newContext.gridField = newField; + newContext.field_2 = newField; }); if (field != null) { - newContext.gridField = field; + newContext.field_2 = field; } if (typeOptionData != null) { @@ -76,35 +75,35 @@ class FieldEditorBloc extends Bloc { FieldService.insertField( gridId: gridId, - field: newContext.gridField, - typeOptionData: newContext.typeOptionData, + field: newFieldTypeOptionData.field_2, + typeOptionData: newFieldTypeOptionData.typeOptionData, ); - return Some(newContext); + return Some(newFieldTypeOptionData); }, ); } Future _saveField(Emitter emit) async { - await state.editFieldContext.fold( + await state.fieldTypeOptionData.fold( () async => null, - (context) async { + (data) async { final result = await FieldService.insertField( gridId: gridId, - field: context.gridField, - typeOptionData: context.typeOptionData, + field: data.field_2, + typeOptionData: data.typeOptionData, ); result.fold((l) => null, (r) => null); }, ); } - Future _getEditFieldContext(Emitter emit) async { + Future _getFieldTypeOptionContext(Emitter emit) async { final result = await _loader.load(); result.fold( (context) { emit(state.copyWith( - editFieldContext: Some(context), + fieldTypeOptionData: Some(context), )); }, (err) => Log.error(err), @@ -125,12 +124,12 @@ class FieldEditorState with _$FieldEditorState { const factory FieldEditorState({ required String gridId, required String errorText, - required Option editFieldContext, + required Option fieldTypeOptionData, }) = _FieldEditorState; factory FieldEditorState.initial(String gridId) => FieldEditorState( gridId: gridId, - editFieldContext: none(), + fieldTypeOptionData: none(), errorText: '', ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart index 09d0268d63..ea10e4222f 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart @@ -7,7 +7,7 @@ import 'dart:async'; part 'field_editor_pannel_bloc.freezed.dart'; class FieldEditorPannelBloc extends Bloc { - FieldEditorPannelBloc(EditFieldContext editContext) : super(FieldEditorPannelState.initial(editContext)) { + FieldEditorPannelBloc(FieldTypeOptionData editContext) : super(FieldEditorPannelState.initial(editContext)) { on( (event, emit) async { await event.map( @@ -45,9 +45,9 @@ class FieldEditorPannelState with _$FieldEditorPannelState { required Uint8List typeOptionData, }) = _FieldEditorPannelState; - factory FieldEditorPannelState.initial(EditFieldContext context) => FieldEditorPannelState( - gridId: context.gridId, - field: context.gridField, - typeOptionData: Uint8List.fromList(context.typeOptionData), + factory FieldEditorPannelState.initial(FieldTypeOptionData data) => FieldEditorPannelState( + gridId: data.gridId, + field: data.field_2, + typeOptionData: Uint8List.fromList(data.typeOptionData), ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart index 70c6caf9f8..a760f13176 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart @@ -12,7 +12,7 @@ class FieldService { FieldService({required this.gridId, required this.fieldId}); - Future> switchToField(FieldType fieldType) { + Future> switchToField(FieldType fieldType) { final payload = EditFieldPayload.create() ..gridId = gridId ..fieldId = fieldId @@ -21,15 +21,6 @@ class FieldService { return GridEventSwitchToField(payload).send(); } - Future> getEditFieldContext(FieldType fieldType) { - final payload = EditFieldPayload.create() - ..gridId = gridId - ..fieldId = fieldId - ..fieldType = fieldType; - - return GridEventGetEditFieldContext(payload).send(); - } - Future> moveField(int fromIndex, int toIndex) { final payload = MoveItemPayload.create() ..gridId = gridId @@ -128,7 +119,7 @@ class FieldService { return GridEventDuplicateField(payload).send(); } - Future, FlowyError>> getTypeOptionData({ + Future> getFieldTypeOptionData({ required FieldType fieldType, }) { final payload = EditFieldPayload.create() @@ -137,7 +128,7 @@ class FieldService { ..fieldType = fieldType; return GridEventGetFieldTypeOption(payload).send().then((result) { return result.fold( - (data) => left(data.typeOptionData), + (data) => left(data), (err) => right(err), ); }); @@ -152,58 +143,58 @@ class GridFieldCellContext with _$GridFieldCellContext { }) = _GridFieldCellContext; } -abstract class EditFieldContextLoader { - Future> load(); +abstract class FieldContextLoader { + Future> load(); - Future> switchToField(String fieldId, FieldType fieldType); + Future> switchToField(String fieldId, FieldType fieldType); } -class NewFieldContextLoader extends EditFieldContextLoader { +class NewFieldContextLoader extends FieldContextLoader { final String gridId; NewFieldContextLoader({ required this.gridId, }); @override - Future> load() { + Future> load() { final payload = EditFieldPayload.create() ..gridId = gridId ..fieldType = FieldType.RichText; - return GridEventGetEditFieldContext(payload).send(); + return GridEventGetFieldTypeOption(payload).send(); } @override - Future> switchToField(String fieldId, FieldType fieldType) { + Future> switchToField(String fieldId, FieldType fieldType) { final payload = EditFieldPayload.create() ..gridId = gridId ..fieldType = fieldType; - return GridEventGetEditFieldContext(payload).send(); + return GridEventGetFieldTypeOption(payload).send(); } } -class FieldContextLoaderAdaptor extends EditFieldContextLoader { +class DefaultFieldContextLoader extends FieldContextLoader { final String gridId; final Field field; - FieldContextLoaderAdaptor({ + DefaultFieldContextLoader({ required this.gridId, required this.field, }); @override - Future> load() { + Future> load() { final payload = EditFieldPayload.create() ..gridId = gridId ..fieldId = field.id ..fieldType = field.fieldType; - return GridEventGetEditFieldContext(payload).send(); + return GridEventGetFieldTypeOption(payload).send(); } @override - Future> switchToField(String fieldId, FieldType fieldType) async { + Future> switchToField(String fieldId, FieldType fieldType) async { final fieldService = FieldService(gridId: gridId, fieldId: fieldId); return fieldService.switchToField(fieldType); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/field_option_pannel_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart similarity index 52% rename from frontend/app_flowy/lib/workspace/application/grid/field/type_option/field_option_pannel_bloc.dart rename to frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart index 04ae02309d..7e1bf9a0d9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/field_option_pannel_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart @@ -3,17 +3,18 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'package:dartz/dartz.dart'; -part 'field_option_pannel_bloc.freezed.dart'; +part 'select_option_type_option_bloc.freezed.dart'; -class FieldOptionPannelBloc extends Bloc { - FieldOptionPannelBloc({required List options}) : super(FieldOptionPannelState.initial(options)) { - on( +class SelectOptionTypeOptionBloc extends Bloc { + SelectOptionTypeOptionBloc({required List options}) + : super(SelectOptionTyepOptionState.initial(options)) { + on( (event, emit) async { await event.map( createOption: (_CreateOption value) async { - emit(state.copyWith(isEditingOption: false, newOptionName: Some(value.optionName))); + emit(state.copyWith(isEditingOption: true, newOptionName: Some(value.optionName))); }, - beginAddingOption: (_BeginAddingOption value) { + addingOption: (_AddingOption value) { emit(state.copyWith(isEditingOption: true, newOptionName: none())); }, endAddingOption: (_EndAddingOption value) { @@ -37,25 +38,25 @@ class FieldOptionPannelBloc extends Bloc options, required bool isEditingOption, required Option newOptionName, required Option updateOption, required Option deleteOption, - }) = _FieldOptionPannelState; + }) = _SelectOptionTyepOptionState; - factory FieldOptionPannelState.initial(List options) => FieldOptionPannelState( + factory SelectOptionTyepOptionState.initial(List options) => SelectOptionTyepOptionState( options: options, isEditingOption: false, newOptionName: none(), diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart index 92f2263c5d..c762f14c1d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; @@ -32,13 +33,58 @@ class TypeOptionService { } } +abstract class TypeOptionDataBuilder { + T fromBuffer(List buffer); +} + class TypeOptionContext { final String gridId; final Field field; final Uint8List data; - const TypeOptionContext({ + + TypeOptionContext({ required this.gridId, required this.field, required this.data, }); } + +abstract class TypeOptionFieldDelegate { + void onFieldChanged(void Function(String) callback); + void dispose(); +} + +class TypeOptionContext2 { + final String gridId; + final Field field; + final FieldService _fieldService; + T? _data; + final TypeOptionDataBuilder dataBuilder; + + TypeOptionContext2({ + required this.gridId, + required this.field, + required this.dataBuilder, + Uint8List? data, + }) : _fieldService = FieldService(gridId: gridId, fieldId: field.id) { + if (data != null) { + _data = dataBuilder.fromBuffer(data); + } + } + + Future> typeOptionData() { + if (_data != null) { + return Future(() => left(_data!)); + } + + return _fieldService.getFieldTypeOptionData(fieldType: field.fieldType).then((result) { + return result.fold( + (data) { + _data = dataBuilder.fromBuffer(data.typeOptionData); + return left(_data!); + }, + (err) => right(err), + ); + }); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart index abf8288c45..016eb00e85 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart @@ -40,7 +40,7 @@ class DateCellEditor with FlowyOverlayDelegate { (data) { final calendar = _CellCalendarWidget( cellContext: cellContext, - dateTypeOption: DateTypeOption.fromBuffer(data), + dateTypeOption: DateTypeOption.fromBuffer(data.typeOptionData), ); FlowyOverlay.of(context).insertWithAnchor( diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index eb264e6664..1e67f4c70b 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -37,7 +37,7 @@ class GridFieldCell extends StatelessWidget { child: _DragToExpandLine(), ); - return _CellContainer( + return _GridHeaderCellContainer( width: state.field.width.toDouble(), child: Stack( alignment: Alignment.centerRight, @@ -63,7 +63,7 @@ class GridFieldCell extends StatelessWidget { FieldEditor( gridId: state.gridId, - fieldContextLoader: FieldContextLoaderAdaptor( + contextLoader: DefaultFieldContextLoader( gridId: state.gridId, field: state.field, ), @@ -71,10 +71,10 @@ class GridFieldCell extends StatelessWidget { } } -class _CellContainer extends StatelessWidget { +class _GridHeaderCellContainer extends StatelessWidget { final Widget child; final double width; - const _CellContainer({ + const _GridHeaderCellContainer({ required this.child, required this.width, Key? key, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart index 11e55e0e06..cc3a12930c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart @@ -14,12 +14,12 @@ import 'field_editor_pannel.dart'; class FieldEditor extends FlowyOverlayDelegate { final String gridId; final FieldEditorBloc _fieldEditorBloc; - final EditFieldContextLoader fieldContextLoader; + final FieldContextLoader contextLoader; FieldEditor({ required this.gridId, - required this.fieldContextLoader, + required this.contextLoader, Key? key, - }) : _fieldEditorBloc = getIt(param1: gridId, param2: fieldContextLoader) { + }) : _fieldEditorBloc = getIt(param1: gridId, param2: contextLoader) { _fieldEditorBloc.add(const FieldEditorEvent.initial()); } @@ -28,9 +28,10 @@ class FieldEditor extends FlowyOverlayDelegate { AnchorDirection anchorDirection = AnchorDirection.bottomWithLeftAligned, }) { FlowyOverlay.of(context).remove(identifier()); + final child = _FieldEditorPage(_fieldEditorBloc, contextLoader); FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( - child: _FieldEditorWidget(_fieldEditorBloc, fieldContextLoader), + child: child, constraints: BoxConstraints.loose(const Size(280, 400)), ), identifier: identifier(), @@ -54,10 +55,10 @@ class FieldEditor extends FlowyOverlayDelegate { bool asBarrier() => true; } -class _FieldEditorWidget extends StatelessWidget { +class _FieldEditorPage extends StatelessWidget { final FieldEditorBloc editorBloc; - final EditFieldContextLoader fieldContextLoader; - const _FieldEditorWidget(this.editorBloc, this.fieldContextLoader, {Key? key}) : super(key: key); + final FieldContextLoader contextLoader; + const _FieldEditorPage(this.editorBloc, this.contextLoader, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -65,9 +66,9 @@ class _FieldEditorWidget extends StatelessWidget { value: editorBloc, child: BlocBuilder( builder: (context, state) { - return state.editFieldContext.fold( + return state.fieldTypeOptionData.fold( () => const SizedBox(), - (editFieldContext) => ListView( + (fieldTypeOptionContext) => ListView( shrinkWrap: true, children: [ FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12), @@ -75,9 +76,9 @@ class _FieldEditorWidget extends StatelessWidget { const _FieldNameTextField(), const VSpace(10), FieldEditorPannel( - editFieldContext: editFieldContext, + fieldTypeOptionData: fieldTypeOptionContext, onSwitchToField: (fieldId, fieldType) { - return fieldContextLoader.switchToField(fieldId, fieldType); + return contextLoader.switchToField(fieldId, fieldType); }, onUpdated: (field, typeOptionData) { context.read().add(FieldEditorEvent.updateField(field, typeOptionData)); @@ -99,9 +100,9 @@ class _FieldNameTextField extends StatelessWidget { Widget build(BuildContext context) { return BlocSelector( selector: (state) { - return state.editFieldContext.fold( + return state.fieldTypeOptionData.fold( () => "", - (editFieldContext) => editFieldContext.gridField.name, + (fieldTypeOptionContext) => fieldTypeOptionContext.field_2.name, ); }, builder: (context, name) { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart index b64cd4ad50..5d96f92655 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart @@ -25,18 +25,18 @@ import 'type_option/number.dart'; import 'type_option/single_select.dart'; typedef UpdateFieldCallback = void Function(Field, Uint8List); -typedef SwitchToFieldCallback = Future> Function( +typedef SwitchToFieldCallback = Future> Function( String fieldId, FieldType fieldType, ); class FieldEditorPannel extends StatefulWidget { - final EditFieldContext editFieldContext; + final FieldTypeOptionData fieldTypeOptionData; final UpdateFieldCallback onUpdated; final SwitchToFieldCallback onSwitchToField; const FieldEditorPannel({ - required this.editFieldContext, + required this.fieldTypeOptionData, required this.onUpdated, required this.onSwitchToField, Key? key, @@ -52,7 +52,7 @@ class _FieldEditorPannelState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => getIt(param1: widget.editFieldContext), + create: (context) => getIt(param1: widget.fieldTypeOptionData), child: BlocConsumer( listener: (context, state) { widget.onUpdated(state.field, state.typeOptionData); @@ -86,11 +86,11 @@ class _FieldEditorPannelState extends State { final list = FieldTypeList(onSelectField: (newFieldType) { widget.onSwitchToField(field.id, newFieldType).then((result) { result.fold( - (editFieldContext) { + (fieldTypeOptionContext) { context.read().add( FieldEditorPannelEvent.toFieldType( - editFieldContext.gridField, - editFieldContext.typeOptionData, + fieldTypeOptionContext.field_2, + fieldTypeOptionContext.typeOptionData, ), ); }, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart index 63a397710c..d7b24b4d8c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart @@ -150,7 +150,7 @@ class CreateFieldButton extends StatelessWidget { hoverColor: theme.hover, onTap: () => FieldEditor( gridId: gridId, - fieldContextLoader: NewFieldContextLoader(gridId: gridId), + contextLoader: NewFieldContextLoader(gridId: gridId), ).show(context), leftIcon: svgWidget("home/add"), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart index 397c5e054d..cd2997738c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart @@ -58,7 +58,7 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { context.read().add(MultiSelectTypeOptionEvent.deleteOption(deleteOption)); }, overlayDelegate: overlayDelegate, - key: ValueKey(state.typeOption.hashCode), + // key: ValueKey(state.typeOption.hashCode), ); }, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart index c00a473b38..a6a76673b3 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart @@ -1,4 +1,4 @@ -import 'package:app_flowy/workspace/application/grid/field/type_option/field_option_pannel_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; @@ -36,8 +36,8 @@ class SelectOptionTypeOptionWidget extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => FieldOptionPannelBloc(options: options), - child: BlocConsumer( + create: (context) => SelectOptionTypeOptionBloc(options: options), + child: BlocConsumer( listener: (context, state) { if (state.isEditingOption) { beginEdit(); @@ -61,18 +61,10 @@ class SelectOptionTypeOptionWidget extends StatelessWidget { List children = [ const TypeOptionSeparator(), const OptionTitle(), + if (state.isEditingOption) const _CreateOptionTextField(), + if (state.options.isEmpty && !state.isEditingOption) const _AddOptionButton(), + _OptionList(overlayDelegate) ]; - if (state.isEditingOption) { - children.add(const _OptionNameTextField()); - } - - if (state.options.isEmpty && !state.isEditingOption) { - children.add(const _AddOptionButton()); - } - - if (state.options.isNotEmpty) { - children.add(_OptionList(overlayDelegate)); - } return Column(children: children); }, @@ -86,12 +78,12 @@ class OptionTitle extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { List children = [FlowyText.medium(LocaleKeys.grid_field_optionTitle.tr(), fontSize: 12)]; if (state.options.isNotEmpty) { children.add(const Spacer()); - children.add(const _OptionTitleAddOptionButton()); + children.add(const _OptionTitleButton()); } return SizedBox( @@ -103,8 +95,8 @@ class OptionTitle extends StatelessWidget { } } -class _OptionTitleAddOptionButton extends StatelessWidget { - const _OptionTitleAddOptionButton({Key? key}) : super(key: key); +class _OptionTitleButton extends StatelessWidget { + const _OptionTitleButton({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -120,7 +112,7 @@ class _OptionTitleAddOptionButton extends StatelessWidget { ), hoverColor: theme.hover, onTap: () { - context.read().add(const FieldOptionPannelEvent.beginAddingOption()); + context.read().add(const SelectOptionTypeOptionEvent.addingOption()); }, ), ); @@ -133,7 +125,7 @@ class _OptionList extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( buildWhen: (previous, current) { return previous.options != current.options; }, @@ -160,16 +152,16 @@ class _OptionList extends StatelessWidget { _OptionCell _makeOptionCell(BuildContext context, SelectOption option) { return _OptionCell( option: option, - onEdited: (option) { + onSelected: (option) { final pannel = SelectOptionTypeOptionEditor( option: option, onDeleted: () { delegate.hideOverlay(context); - context.read().add(FieldOptionPannelEvent.deleteOption(option)); + context.read().add(SelectOptionTypeOptionEvent.deleteOption(option)); }, onUpdated: (updatedOption) { delegate.hideOverlay(context); - context.read().add(FieldOptionPannelEvent.updateOption(updatedOption)); + context.read().add(SelectOptionTypeOptionEvent.updateOption(updatedOption)); }, key: ValueKey(option.id), ); @@ -181,10 +173,10 @@ class _OptionList extends StatelessWidget { class _OptionCell extends StatelessWidget { final SelectOption option; - final Function(SelectOption) onEdited; + final Function(SelectOption) onSelected; const _OptionCell({ required this.option, - required this.onEdited, + required this.onSelected, Key? key, }) : super(key: key); @@ -196,7 +188,7 @@ class _OptionCell extends StatelessWidget { child: FlowyButton( text: FlowyText.medium(option.name, fontSize: 12), hoverColor: theme.hover, - onTap: () => onEdited(option), + onTap: () => onSelected(option), rightIcon: svgWidget("grid/details", color: theme.iconColor), ), ); @@ -215,7 +207,7 @@ class _AddOptionButton extends StatelessWidget { text: FlowyText.medium(LocaleKeys.grid_field_addSelectOption.tr(), fontSize: 12), hoverColor: theme.hover, onTap: () { - context.read().add(const FieldOptionPannelEvent.beginAddingOption()); + context.read().add(const SelectOptionTypeOptionEvent.addingOption()); }, leftIcon: svgWidget("home/add", color: theme.iconColor), ), @@ -223,18 +215,18 @@ class _AddOptionButton extends StatelessWidget { } } -class _OptionNameTextField extends StatelessWidget { - const _OptionNameTextField({Key? key}) : super(key: key); +class _CreateOptionTextField extends StatelessWidget { + const _CreateOptionTextField({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return InputTextField( text: "", onCanceled: () { - context.read().add(const FieldOptionPannelEvent.endAddingOption()); + context.read().add(const SelectOptionTypeOptionEvent.endAddingOption()); }, onDone: (optionName) { - context.read().add(FieldOptionPannelEvent.createOption(optionName)); + context.read().add(SelectOptionTypeOptionEvent.createOption(optionName)); }, ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart index 3b84eec495..11793fbb43 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart @@ -57,7 +57,7 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget { context.read().add(SingleSelectTypeOptionEvent.deleteOption(deleteOption)); }, overlayDelegate: overlayDelegate, - key: ValueKey(state.typeOption.hashCode), + // key: ValueKey(state.typeOption.hashCode), ); }, ), 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 a6f86cafee..474a21773d 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 @@ -178,7 +178,7 @@ class _RowDetailCell extends StatelessWidget { void _showFieldEditor(BuildContext context) { FieldEditor( gridId: gridCell.gridId, - fieldContextLoader: FieldContextLoaderAdaptor( + contextLoader: DefaultFieldContextLoader( gridId: gridCell.gridId, field: gridCell.field, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart index aa8f88ab5d..151afc1e3f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart @@ -115,7 +115,7 @@ class _GridPropertyCell extends StatelessWidget { onTap: () { FieldEditor( gridId: gridId, - fieldContextLoader: FieldContextLoaderAdaptor(gridId: gridId, field: field), + contextLoader: DefaultFieldContextLoader(gridId: gridId, field: field), ).show(context, anchorDirection: AnchorDirection.bottomRight); }, ); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart index a1208075f5..95c17f46cf 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart @@ -124,14 +124,14 @@ class GridEventSwitchToField { EditFieldPayload request; GridEventSwitchToField(this.request); - Future> send() { + Future> send() { final request = FFIRequest.create() ..event = GridEvent.SwitchToField.toString() ..payload = requestToBytes(this.request); return Dispatch.asyncRequest(request) .then((bytesResult) => bytesResult.fold( - (okBytes) => left(EditFieldContext.fromBuffer(okBytes)), + (okBytes) => left(FieldTypeOptionData.fromBuffer(okBytes)), (errBytes) => right(FlowyError.fromBuffer(errBytes)), )); } @@ -154,23 +154,6 @@ class GridEventDuplicateField { } } -class GridEventGetEditFieldContext { - EditFieldPayload request; - GridEventGetEditFieldContext(this.request); - - Future> send() { - final request = FFIRequest.create() - ..event = GridEvent.GetEditFieldContext.toString() - ..payload = requestToBytes(this.request); - - return Dispatch.asyncRequest(request) - .then((bytesResult) => bytesResult.fold( - (okBytes) => left(EditFieldContext.fromBuffer(okBytes)), - (errBytes) => right(FlowyError.fromBuffer(errBytes)), - )); - } -} - class GridEventMoveItem { MoveItemPayload request; GridEventMoveItem(this.request); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index 4c18294dae..ae450b2e6d 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -578,16 +578,16 @@ class EditFieldPayload extends $pb.GeneratedMessage { void clearFieldType() => clearField(3); } -class EditFieldContext extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldContext', createEmptyInstance: create) +class FieldTypeOptionContext extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldTypeOptionContext', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId') ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridField', subBuilder: Field.create) ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY) ..hasRequiredFields = false ; - EditFieldContext._() : super(); - factory EditFieldContext({ + FieldTypeOptionContext._() : super(); + factory FieldTypeOptionContext({ $core.String? gridId, Field? gridField, $core.List<$core.int>? typeOptionData, @@ -604,26 +604,26 @@ class EditFieldContext extends $pb.GeneratedMessage { } return _result; } - factory EditFieldContext.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory EditFieldContext.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory FieldTypeOptionContext.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory FieldTypeOptionContext.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - EditFieldContext clone() => EditFieldContext()..mergeFromMessage(this); + FieldTypeOptionContext clone() => FieldTypeOptionContext()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - EditFieldContext copyWith(void Function(EditFieldContext) updates) => super.copyWith((message) => updates(message as EditFieldContext)) as EditFieldContext; // ignore: deprecated_member_use + FieldTypeOptionContext copyWith(void Function(FieldTypeOptionContext) updates) => super.copyWith((message) => updates(message as FieldTypeOptionContext)) as FieldTypeOptionContext; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static EditFieldContext create() => EditFieldContext._(); - EditFieldContext createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static FieldTypeOptionContext create() => FieldTypeOptionContext._(); + FieldTypeOptionContext createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static EditFieldContext getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static EditFieldContext? _defaultInstance; + static FieldTypeOptionContext getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static FieldTypeOptionContext? _defaultInstance; @$pb.TagNumber(1) $core.String get gridId => $_getSZ(0); @@ -657,19 +657,24 @@ class EditFieldContext extends $pb.GeneratedMessage { class FieldTypeOptionData extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldTypeOptionData', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') - ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId') + ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'field', subBuilder: Field.create) + ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY) ..hasRequiredFields = false ; FieldTypeOptionData._() : super(); factory FieldTypeOptionData({ - $core.String? fieldId, + $core.String? gridId, + Field? field_2, $core.List<$core.int>? typeOptionData, }) { final _result = create(); - if (fieldId != null) { - _result.fieldId = fieldId; + if (gridId != null) { + _result.gridId = gridId; + } + if (field_2 != null) { + _result.field_2 = field_2; } if (typeOptionData != null) { _result.typeOptionData = typeOptionData; @@ -698,22 +703,33 @@ class FieldTypeOptionData extends $pb.GeneratedMessage { static FieldTypeOptionData? _defaultInstance; @$pb.TagNumber(1) - $core.String get fieldId => $_getSZ(0); + $core.String get gridId => $_getSZ(0); @$pb.TagNumber(1) - set fieldId($core.String v) { $_setString(0, v); } + set gridId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) - $core.bool hasFieldId() => $_has(0); + $core.bool hasGridId() => $_has(0); @$pb.TagNumber(1) - void clearFieldId() => clearField(1); + void clearGridId() => clearField(1); @$pb.TagNumber(2) - $core.List<$core.int> get typeOptionData => $_getN(1); + Field get field_2 => $_getN(1); @$pb.TagNumber(2) - set typeOptionData($core.List<$core.int> v) { $_setBytes(1, v); } + set field_2(Field v) { setField(2, v); } @$pb.TagNumber(2) - $core.bool hasTypeOptionData() => $_has(1); + $core.bool hasField_2() => $_has(1); @$pb.TagNumber(2) - void clearTypeOptionData() => clearField(2); + void clearField_2() => clearField(2); + @$pb.TagNumber(2) + Field ensureField_2() => $_ensure(1); + + @$pb.TagNumber(3) + $core.List<$core.int> get typeOptionData => $_getN(2); + @$pb.TagNumber(3) + set typeOptionData($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(3) + $core.bool hasTypeOptionData() => $_has(2); + @$pb.TagNumber(3) + void clearTypeOptionData() => clearField(3); } class RepeatedField extends $pb.GeneratedMessage { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index 3155b79a2e..78e834edb7 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -127,9 +127,9 @@ const EditFieldPayload$json = const { /// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIbCghmaWVsZF9pZBgCIAEoCUgAUgdmaWVsZElkEikKCmZpZWxkX3R5cGUYAyABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZUIRCg9vbmVfb2ZfZmllbGRfaWQ='); -@$core.Deprecated('Use editFieldContextDescriptor instead') -const EditFieldContext$json = const { - '1': 'EditFieldContext', +@$core.Deprecated('Use fieldTypeOptionContextDescriptor instead') +const FieldTypeOptionContext$json = const { + '1': 'FieldTypeOptionContext', '2': const [ const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'}, const {'1': 'grid_field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'gridField'}, @@ -137,19 +137,20 @@ const EditFieldContext$json = const { ], }; -/// Descriptor for `EditFieldContext`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List editFieldContextDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRDb250ZXh0EhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIlCgpncmlkX2ZpZWxkGAIgASgLMgYuRmllbGRSCWdyaWRGaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ=='); +/// Descriptor for `FieldTypeOptionContext`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List fieldTypeOptionContextDescriptor = $convert.base64Decode('ChZGaWVsZFR5cGVPcHRpb25Db250ZXh0EhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIlCgpncmlkX2ZpZWxkGAIgASgLMgYuRmllbGRSCWdyaWRGaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ=='); @$core.Deprecated('Use fieldTypeOptionDataDescriptor instead') const FieldTypeOptionData$json = const { '1': 'FieldTypeOptionData', '2': const [ - const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'}, - const {'1': 'type_option_data', '3': 2, '4': 1, '5': 12, '10': 'typeOptionData'}, + const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'}, + const {'1': 'field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'field'}, + const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'}, ], }; /// Descriptor for `FieldTypeOptionData`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List fieldTypeOptionDataDescriptor = $convert.base64Decode('ChNGaWVsZFR5cGVPcHRpb25EYXRhEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEigKEHR5cGVfb3B0aW9uX2RhdGEYAiABKAxSDnR5cGVPcHRpb25EYXRh'); +final $typed_data.Uint8List fieldTypeOptionDataDescriptor = $convert.base64Decode('ChNGaWVsZFR5cGVPcHRpb25EYXRhEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIcCgVmaWVsZBgCIAEoCzIGLkZpZWxkUgVmaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ=='); @$core.Deprecated('Use repeatedFieldDescriptor instead') const RepeatedField$json = const { '1': 'RepeatedField', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart index 0035c0f24d..f9945ab604 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart @@ -19,9 +19,8 @@ class GridEvent extends $pb.ProtobufEnum { static const GridEvent DeleteField = GridEvent._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteField'); static const GridEvent SwitchToField = GridEvent._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SwitchToField'); static const GridEvent DuplicateField = GridEvent._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField'); - static const GridEvent GetEditFieldContext = GridEvent._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetEditFieldContext'); - static const GridEvent MoveItem = GridEvent._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveItem'); - static const GridEvent GetFieldTypeOption = GridEvent._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFieldTypeOption'); + static const GridEvent MoveItem = GridEvent._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveItem'); + static const GridEvent GetFieldTypeOption = GridEvent._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFieldTypeOption'); static const GridEvent NewSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewSelectOption'); static const GridEvent GetSelectOptionCellData = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptionCellData'); static const GridEvent UpdateSelectOption = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOption'); @@ -45,7 +44,6 @@ class GridEvent extends $pb.ProtobufEnum { DeleteField, SwitchToField, DuplicateField, - GetEditFieldContext, MoveItem, GetFieldTypeOption, NewSelectOption, diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart index 5088625002..89842a54d0 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart @@ -21,9 +21,8 @@ const GridEvent$json = const { const {'1': 'DeleteField', '2': 14}, const {'1': 'SwitchToField', '2': 20}, const {'1': 'DuplicateField', '2': 21}, - const {'1': 'GetEditFieldContext', '2': 22}, - const {'1': 'MoveItem', '2': 23}, - const {'1': 'GetFieldTypeOption', '2': 24}, + const {'1': 'MoveItem', '2': 22}, + const {'1': 'GetFieldTypeOption', '2': 23}, const {'1': 'NewSelectOption', '2': 30}, const {'1': 'GetSelectOptionCellData', '2': 31}, const {'1': 'UpdateSelectOption', '2': 32}, @@ -40,4 +39,4 @@ const GridEvent$json = const { }; /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAWEgwKCE1vdmVJdGVtEBcSFgoSR2V0RmllbGRUeXBlT3B0aW9uEBgSEwoPTmV3U2VsZWN0T3B0aW9uEB4SGwoXR2V0U2VsZWN0T3B0aW9uQ2VsbERhdGEQHxIWChJVcGRhdGVTZWxlY3RPcHRpb24QIBINCglDcmVhdGVSb3cQMhIKCgZHZXRSb3cQMxINCglEZWxldGVSb3cQNBIQCgxEdXBsaWNhdGVSb3cQNRILCgdHZXRDZWxsEEYSDgoKVXBkYXRlQ2VsbBBHEhoKFlVwZGF0ZVNlbGVjdE9wdGlvbkNlbGwQSBISCg5VcGRhdGVEYXRlQ2VsbBBQEhMKD0dldERhdGVDZWxsRGF0YRBa'); +final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo='); diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 6e34361961..a8c8b99e68 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -98,7 +98,7 @@ pub(crate) async fn delete_field_handler( pub(crate) async fn switch_to_field_handler( data: Data, manager: AppData>, -) -> DataResult { +) -> DataResult { let params: EditFieldParams = data.into_inner().try_into()?; if params.field_id.is_none() { return Err(ErrorCode::FieldIdIsEmpty.into()); @@ -107,9 +107,9 @@ pub(crate) async fn switch_to_field_handler( let editor = manager.get_grid_editor(¶ms.grid_id)?; editor.switch_to_field_type(&field_id, ¶ms.field_type).await?; let field_meta = editor.get_field_meta(&field_id).await; - let edit_context = - make_edit_field_context(¶ms.grid_id, Some(field_id), params.field_type, editor, field_meta).await?; - data_result(edit_context) + let data = + make_field_type_option_data(¶ms.grid_id, Some(field_id), params.field_type, editor, field_meta).await?; + data_result(data) } #[tracing::instrument(level = "debug", skip(data, manager), err)] @@ -123,19 +123,6 @@ pub(crate) async fn duplicate_field_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] -pub(crate) async fn get_field_context_handler( - data: Data, - manager: AppData>, -) -> DataResult { - let params: EditFieldParams = data.into_inner().try_into()?; - let editor = manager.get_grid_editor(¶ms.grid_id)?; - let edit_context = - make_edit_field_context(¶ms.grid_id, params.field_id, params.field_type, editor, None).await?; - - data_result(edit_context) -} - #[tracing::instrument(level = "debug", skip(data, manager), err)] pub(crate) async fn get_field_type_option_data_handler( data: Data, @@ -147,7 +134,8 @@ pub(crate) async fn get_field_type_option_data_handler( let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?; data_result(FieldTypeOptionData { - field_id: field_meta.id.clone(), + grid_id: params.grid_id, + field: field_meta.into(), type_option_data, }) } @@ -163,23 +151,24 @@ pub(crate) async fn move_item_handler( Ok(()) } -async fn make_edit_field_context( +async fn make_field_type_option_data( grid_id: &str, field_id: Option, field_type: FieldType, editor: Arc, field_meta: Option, -) -> FlowyResult { +) -> FlowyResult { let field_meta = field_meta.unwrap_or(get_or_create_field_meta(field_id, &field_type, editor).await?); let type_option_data = get_type_option_data(&field_meta, &field_type).await?; - let field: Field = field_meta.into(); - Ok(EditFieldContext { + + Ok(FieldTypeOptionData { grid_id: grid_id.to_string(), - grid_field: field, + field: field_meta.into(), type_option_data, }) } +/// The FieldMeta contains multiple data, each of them belongs to a specific FieldType. async fn get_type_option_data(field_meta: &FieldMeta, field_type: &FieldType) -> FlowyResult> { let s = field_meta .get_type_option_str(field_type) diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index 2d29d7f096..fe749262ef 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -18,7 +18,6 @@ pub fn create(grid_manager: Arc) -> Module { .event(GridEvent::DeleteField, delete_field_handler) .event(GridEvent::SwitchToField, switch_to_field_handler) .event(GridEvent::DuplicateField, duplicate_field_handler) - .event(GridEvent::GetEditFieldContext, get_field_context_handler) .event(GridEvent::MoveItem, move_item_handler) .event(GridEvent::GetFieldTypeOption, get_field_type_option_data_handler) // Row @@ -65,20 +64,17 @@ pub enum GridEvent { #[event(input = "FieldIdentifierPayload")] DeleteField = 14, - #[event(input = "EditFieldPayload", output = "EditFieldContext")] + #[event(input = "EditFieldPayload", output = "FieldTypeOptionData")] SwitchToField = 20, #[event(input = "FieldIdentifierPayload")] DuplicateField = 21, - #[event(input = "EditFieldPayload", output = "EditFieldContext")] - GetEditFieldContext = 22, - #[event(input = "MoveItemPayload")] - MoveItem = 23, + MoveItem = 22, #[event(input = "EditFieldPayload", output = "FieldTypeOptionData")] - GetFieldTypeOption = 24, + GetFieldTypeOption = 23, #[event(input = "CreateSelectOptionPayload", output = "SelectOption")] NewSelectOption = 30, diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs index bb7494ec3e..f6c4861c29 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs @@ -34,9 +34,8 @@ pub enum GridEvent { DeleteField = 14, SwitchToField = 20, DuplicateField = 21, - GetEditFieldContext = 22, - MoveItem = 23, - GetFieldTypeOption = 24, + MoveItem = 22, + GetFieldTypeOption = 23, NewSelectOption = 30, GetSelectOptionCellData = 31, UpdateSelectOption = 32, @@ -67,9 +66,8 @@ impl ::protobuf::ProtobufEnum for GridEvent { 14 => ::std::option::Option::Some(GridEvent::DeleteField), 20 => ::std::option::Option::Some(GridEvent::SwitchToField), 21 => ::std::option::Option::Some(GridEvent::DuplicateField), - 22 => ::std::option::Option::Some(GridEvent::GetEditFieldContext), - 23 => ::std::option::Option::Some(GridEvent::MoveItem), - 24 => ::std::option::Option::Some(GridEvent::GetFieldTypeOption), + 22 => ::std::option::Option::Some(GridEvent::MoveItem), + 23 => ::std::option::Option::Some(GridEvent::GetFieldTypeOption), 30 => ::std::option::Option::Some(GridEvent::NewSelectOption), 31 => ::std::option::Option::Some(GridEvent::GetSelectOptionCellData), 32 => ::std::option::Option::Some(GridEvent::UpdateSelectOption), @@ -97,7 +95,6 @@ impl ::protobuf::ProtobufEnum for GridEvent { GridEvent::DeleteField, GridEvent::SwitchToField, GridEvent::DuplicateField, - GridEvent::GetEditFieldContext, GridEvent::MoveItem, GridEvent::GetFieldTypeOption, GridEvent::NewSelectOption, @@ -140,18 +137,18 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fevent_map.proto*\xda\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ + \n\x0fevent_map.proto*\xc1\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\ \x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\ \x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\ - SwitchToField\x10\x14\x12\x12\n\x0eDuplicateField\x10\x15\x12\x17\n\x13G\ - etEditFieldContext\x10\x16\x12\x0c\n\x08MoveItem\x10\x17\x12\x16\n\x12Ge\ - tFieldTypeOption\x10\x18\x12\x13\n\x0fNewSelectOption\x10\x1e\x12\x1b\n\ - \x17GetSelectOptionCellData\x10\x1f\x12\x16\n\x12UpdateSelectOption\x10\ - \x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteRow\ - \x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\n\ - \nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\x0e\ - UpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\ + SwitchToField\x10\x14\x12\x12\n\x0eDuplicateField\x10\x15\x12\x0c\n\x08M\ + oveItem\x10\x16\x12\x16\n\x12GetFieldTypeOption\x10\x17\x12\x13\n\x0fNew\ + SelectOption\x10\x1e\x12\x1b\n\x17GetSelectOptionCellData\x10\x1f\x12\ + \x16\n\x12UpdateSelectOption\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06\ + GetRow\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\ + \x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSel\ + ectOptionCell\x10H\x12\x12\n\x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDate\ + CellData\x10Zb\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto index 02f621b29e..a0c88a7187 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto @@ -10,9 +10,8 @@ enum GridEvent { DeleteField = 14; SwitchToField = 20; DuplicateField = 21; - GetEditFieldContext = 22; - MoveItem = 23; - GetFieldTypeOption = 24; + MoveItem = 22; + GetFieldTypeOption = 23; NewSelectOption = 30; GetSelectOptionCellData = 31; UpdateSelectOption = 32; 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 0dae40658c..ccbaff82e9 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -195,7 +195,7 @@ impl TryInto for EditFieldPayload { } #[derive(Debug, Default, ProtoBuf)] -pub struct EditFieldContext { +pub struct FieldTypeOptionContext { #[pb(index = 1)] pub grid_id: String, @@ -209,9 +209,12 @@ pub struct EditFieldContext { #[derive(Debug, Default, ProtoBuf)] pub struct FieldTypeOptionData { #[pb(index = 1)] - pub field_id: String, + pub grid_id: String, #[pb(index = 2)] + pub field_id: String, + + #[pb(index = 3)] pub type_option_data: Vec, } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 614d0e8b21..c0c03f65a7 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -1913,7 +1913,7 @@ impl ::protobuf::reflect::ProtobufValue for EditFieldPayload { } #[derive(PartialEq,Clone,Default)] -pub struct EditFieldContext { +pub struct FieldTypeOptionContext { // message fields pub grid_id: ::std::string::String, pub grid_field: ::protobuf::SingularPtrField, @@ -1923,14 +1923,14 @@ pub struct EditFieldContext { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a EditFieldContext { - fn default() -> &'a EditFieldContext { - ::default_instance() +impl<'a> ::std::default::Default for &'a FieldTypeOptionContext { + fn default() -> &'a FieldTypeOptionContext { + ::default_instance() } } -impl EditFieldContext { - pub fn new() -> EditFieldContext { +impl FieldTypeOptionContext { + pub fn new() -> FieldTypeOptionContext { ::std::default::Default::default() } @@ -2020,7 +2020,7 @@ impl EditFieldContext { } } -impl ::protobuf::Message for EditFieldContext { +impl ::protobuf::Message for FieldTypeOptionContext { fn is_initialized(&self) -> bool { for v in &self.grid_field { if !v.is_initialized() { @@ -2112,8 +2112,8 @@ impl ::protobuf::Message for EditFieldContext { Self::descriptor_static() } - fn new() -> EditFieldContext { - EditFieldContext::new() + fn new() -> FieldTypeOptionContext { + FieldTypeOptionContext::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -2122,34 +2122,34 @@ impl ::protobuf::Message for EditFieldContext { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "grid_id", - |m: &EditFieldContext| { &m.grid_id }, - |m: &mut EditFieldContext| { &mut m.grid_id }, + |m: &FieldTypeOptionContext| { &m.grid_id }, + |m: &mut FieldTypeOptionContext| { &mut m.grid_id }, )); fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "grid_field", - |m: &EditFieldContext| { &m.grid_field }, - |m: &mut EditFieldContext| { &mut m.grid_field }, + |m: &FieldTypeOptionContext| { &m.grid_field }, + |m: &mut FieldTypeOptionContext| { &mut m.grid_field }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( "type_option_data", - |m: &EditFieldContext| { &m.type_option_data }, - |m: &mut EditFieldContext| { &mut m.type_option_data }, + |m: &FieldTypeOptionContext| { &m.type_option_data }, + |m: &mut FieldTypeOptionContext| { &mut m.type_option_data }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "EditFieldContext", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "FieldTypeOptionContext", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static EditFieldContext { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(EditFieldContext::new) + fn default_instance() -> &'static FieldTypeOptionContext { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(FieldTypeOptionContext::new) } } -impl ::protobuf::Clear for EditFieldContext { +impl ::protobuf::Clear for FieldTypeOptionContext { fn clear(&mut self) { self.grid_id.clear(); self.grid_field.clear(); @@ -2158,13 +2158,13 @@ impl ::protobuf::Clear for EditFieldContext { } } -impl ::std::fmt::Debug for EditFieldContext { +impl ::std::fmt::Debug for FieldTypeOptionContext { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for EditFieldContext { +impl ::protobuf::reflect::ProtobufValue for FieldTypeOptionContext { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } @@ -2173,6 +2173,7 @@ impl ::protobuf::reflect::ProtobufValue for EditFieldContext { #[derive(PartialEq,Clone,Default)] pub struct FieldTypeOptionData { // message fields + pub grid_id: ::std::string::String, pub field_id: ::std::string::String, pub type_option_data: ::std::vec::Vec, // special fields @@ -2191,7 +2192,33 @@ impl FieldTypeOptionData { ::std::default::Default::default() } - // string field_id = 1; + // string grid_id = 1; + + + pub fn get_grid_id(&self) -> &str { + &self.grid_id + } + pub fn clear_grid_id(&mut self) { + self.grid_id.clear(); + } + + // Param is passed by value, moved + pub fn set_grid_id(&mut self, v: ::std::string::String) { + self.grid_id = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_grid_id(&mut self) -> &mut ::std::string::String { + &mut self.grid_id + } + + // Take field + pub fn take_grid_id(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.grid_id, ::std::string::String::new()) + } + + // string field_id = 2; pub fn get_field_id(&self) -> &str { @@ -2217,7 +2244,7 @@ impl FieldTypeOptionData { ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) } - // bytes type_option_data = 2; + // bytes type_option_data = 3; pub fn get_type_option_data(&self) -> &[u8] { @@ -2254,9 +2281,12 @@ impl ::protobuf::Message for FieldTypeOptionData { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?; }, 2 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; + }, + 3 => { ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.type_option_data)?; }, _ => { @@ -2271,11 +2301,14 @@ impl ::protobuf::Message for FieldTypeOptionData { #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; + if !self.grid_id.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.grid_id); + } if !self.field_id.is_empty() { - my_size += ::protobuf::rt::string_size(1, &self.field_id); + my_size += ::protobuf::rt::string_size(2, &self.field_id); } if !self.type_option_data.is_empty() { - my_size += ::protobuf::rt::bytes_size(2, &self.type_option_data); + my_size += ::protobuf::rt::bytes_size(3, &self.type_option_data); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -2283,11 +2316,14 @@ impl ::protobuf::Message for FieldTypeOptionData { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.grid_id.is_empty() { + os.write_string(1, &self.grid_id)?; + } if !self.field_id.is_empty() { - os.write_string(1, &self.field_id)?; + os.write_string(2, &self.field_id)?; } if !self.type_option_data.is_empty() { - os.write_bytes(2, &self.type_option_data)?; + os.write_bytes(3, &self.type_option_data)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -2327,6 +2363,11 @@ impl ::protobuf::Message for FieldTypeOptionData { static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "grid_id", + |m: &FieldTypeOptionData| { &m.grid_id }, + |m: &mut FieldTypeOptionData| { &mut m.grid_id }, + )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "field_id", |m: &FieldTypeOptionData| { &m.field_id }, @@ -2353,6 +2394,7 @@ impl ::protobuf::Message for FieldTypeOptionData { impl ::protobuf::Clear for FieldTypeOptionData { fn clear(&mut self) { + self.grid_id.clear(); self.field_id.clear(); self.type_option_data.clear(); self.unknown_fields.clear(); @@ -8257,27 +8299,28 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x86\x01\n\x10EditFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ \x06gridId\x12\x1b\n\x08field_id\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\ \n\nfield_type\x18\x03\x20\x01(\x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fo\ - ne_of_field_id\"|\n\x10EditFieldContext\x12\x17\n\x07grid_id\x18\x01\x20\ - \x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\x06.FieldR\t\ - gridField\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOption\ - Data\"Z\n\x13FieldTypeOptionData\x12\x19\n\x08field_id\x18\x01\x20\x01(\ - \tR\x07fieldId\x12(\n\x10type_option_data\x18\x02\x20\x01(\x0cR\x0etypeO\ - ptionData\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\ - \x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\ - \x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06ro\ - w_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\ - \tR\x07blockId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\"\xb8\ - \x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_b\ - y_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFiel\ - dId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellBy\ - FieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05va\ - lue\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedR\ - ow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Re\ - peatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\ - \x05items\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\ - \tR\x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ - Orders\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.Row\ - OrderR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\ + ne_of_field_id\"\x82\x01\n\x16FieldTypeOptionContext\x12\x17\n\x07grid_i\ + d\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\ + \x06.FieldR\tgridField\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\ + \x0etypeOptionData\"s\n\x13FieldTypeOptionData\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07\ + fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionDa\ + ta\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.Fiel\ + dR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\ + \x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\ + \x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07b\ + lockId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\"\xb8\x01\n\ + \x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_fiel\ + d_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFieldId\ + \x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellByFie\ + ldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\ + \x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\ + \x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Repe\ + atedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05i\ + tems\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\ + \x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrd\ + ers\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrd\ + erR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\ \x0e\n\x0cone_of_index\"Q\n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\ \x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\ \x0b2\x04.RowR\x03row\"\xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08bloc\ diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index 5913b0ca0e..c1aa74b0e7 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -38,14 +38,15 @@ message EditFieldPayload { oneof one_of_field_id { string field_id = 2; }; FieldType field_type = 3; } -message EditFieldContext { +message FieldTypeOptionContext { string grid_id = 1; Field grid_field = 2; bytes type_option_data = 3; } message FieldTypeOptionData { - string field_id = 1; - bytes type_option_data = 2; + string grid_id = 1; + string field_id = 2; + bytes type_option_data = 3; } message RepeatedField { repeated Field items = 1; diff --git a/shared-lib/lib-infra/Cargo.toml b/shared-lib/lib-infra/Cargo.toml index 9659490959..8b74a68a4c 100644 --- a/shared-lib/lib-infra/Cargo.toml +++ b/shared-lib/lib-infra/Cargo.toml @@ -16,6 +16,7 @@ rand = "0.8.5" serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" + cmd_lib = { version = "1", optional = true } protoc-rust = { version = "2", optional = true } walkdir = { version = "2", optional = true } 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 ff1f5fa5f5..a65d4d5bd9 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 @@ -7,6 +7,7 @@ mod proto_info; mod template; use crate::code_gen::util::path_string_with_component; +use itertools::Itertools; use log::info; pub use proto_gen::*; pub use proto_info::*; @@ -132,7 +133,7 @@ fn generate_dart_protobuf_files( } } -fn check_pb_dart_plugin() { +pub fn check_pb_dart_plugin() { if cfg!(target_os = "windows") { //Command::new("cmd") // .arg("/C") @@ -141,15 +142,39 @@ fn check_pb_dart_plugin() { // .expect("failed to execute process"); //panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly.")) } else { - let is_success = Command::new("sh") + let exit_result = Command::new("sh") .arg("-c") .arg("command -v protoc-gen-dart") .status() - .expect("failed to execute process") - .success(); + .expect("failed to execute process"); - if !is_success { - panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly. \n✅ You can fix that by adding \"{}\" to your shell's config file.(.bashrc, .bash, etc.)", "dart pub global activate protoc_plugin")) + if !exit_result.success() { + let mut msg = "\n❌ Can't find protoc-gen-dart in $PATH:\n".to_string(); + let output = Command::new("sh").arg("-c").arg("echo $PATH").output(); + let paths = String::from_utf8(output.unwrap().stdout) + .unwrap() + .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(_) => {} + } + + 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.)" + )); + panic!("{}", msg) } } } From 4840d23fff6793bd0e4344b68b76600ae91924c5 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 18 May 2022 15:43:10 +0800 Subject: [PATCH 03/82] chore: add GridFieldContext --- .../app_flowy/lib/startup/deps_resolver.dart | 12 -- .../cell/cell_service/context_builder.dart | 15 ++- .../grid/cell/cell_service/data_loader.dart | 37 +++--- .../grid/field/field_editor_bloc.dart | 115 ++++-------------- .../grid/field/field_editor_pannel_bloc.dart | 40 +++--- .../application/grid/field/field_service.dart | 111 +++++++++++++++++ .../type_option/type_option_service.dart | 16 +-- .../select_option_editor.dart | 70 ++++++----- .../grid/src/widgets/header/field_cell.dart | 1 + .../grid/src/widgets/header/field_editor.dart | 99 +++++++-------- .../widgets/header/field_editor_pannel.dart | 41 ++----- .../grid/src/widgets/header/grid_header.dart | 1 + .../grid/src/widgets/row/row_detail.dart | 1 + .../src/widgets/toolbar/grid_property.dart | 1 + .../lib/style_widget/hover.dart | 13 +- .../src/entities/grid.rs | 2 +- .../src/protobuf/model/grid.rs | 97 ++++++++------- .../src/protobuf/proto/grid.proto | 2 +- 18 files changed, 351 insertions(+), 323 deletions(-) diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 7e65130fa8..698cc63531 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -15,7 +15,6 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/menu/menu.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldTypeOptionData; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; @@ -157,13 +156,6 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( - (gridId, fieldLoader) => FieldEditorBloc( - gridId: gridId, - fieldLoader: fieldLoader, - ), - ); - getIt.registerFactoryParam( (context, _) => TextCellBloc( cellContext: context, @@ -195,10 +187,6 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( - (context, _) => FieldEditorPannelBloc(context), - ); - getIt.registerFactoryParam( (typeOption, _) => DateTypeOptionBloc(typeOption: typeOption), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 4f81e119fc..ed191c7d60 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -19,7 +19,7 @@ class GridCellContextBuilder { return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: CellDataLoader(gridCell: _gridCell), + cellDataLoader: GridCellDataLoader(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.DateTime: @@ -30,17 +30,24 @@ class GridCellContextBuilder { cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), ); case FieldType.Number: + final cellDataLoader = GridCellDataLoader( + gridCell: _gridCell, + config: const GridCellDataConfig( + reloadOnCellChanged: true, + reloadOnFieldChanged: true, + ), + ); return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: CellDataLoader(gridCell: _gridCell, reloadOnCellChanged: true), + cellDataLoader: cellDataLoader, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.RichText: return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: CellDataLoader(gridCell: _gridCell), + cellDataLoader: GridCellDataLoader(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.MultiSelect: @@ -62,7 +69,7 @@ class _GridCellContext extends Equatable { final GridCell gridCell; final GridCellCache cellCache; final GridCellCacheKey _cacheKey; - final _GridCellDataLoader cellDataLoader; + final IGridCellDataLoader cellDataLoader; final _GridCellDataPersistence cellDataPersistence; final FieldService _fieldService; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart index f625af9ad0..5be0435323 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart @@ -1,6 +1,6 @@ part of 'cell_service.dart'; -abstract class GridCellDataConfig { +abstract class IGridCellDataConfig { // The cell data will reload if it receives the field's change notification. bool get reloadOnFieldChanged; @@ -11,34 +11,36 @@ abstract class GridCellDataConfig { bool get reloadOnCellChanged; } -class DefaultCellDataConfig implements GridCellDataConfig { +class GridCellDataConfig implements IGridCellDataConfig { @override final bool reloadOnCellChanged; @override final bool reloadOnFieldChanged; - DefaultCellDataConfig({ + const GridCellDataConfig({ this.reloadOnCellChanged = false, this.reloadOnFieldChanged = false, }); } -abstract class _GridCellDataLoader { +abstract class IGridCellDataLoader { Future loadData(); - GridCellDataConfig get config; + IGridCellDataConfig get config; } -class CellDataLoader extends _GridCellDataLoader { +class GridCellDataLoader extends IGridCellDataLoader { final CellService service = CellService(); final GridCell gridCell; - final GridCellDataConfig _config; - CellDataLoader({ + @override + final IGridCellDataConfig config; + + GridCellDataLoader({ required this.gridCell, - bool reloadOnCellChanged = false, - }) : _config = DefaultCellDataConfig(reloadOnCellChanged: reloadOnCellChanged); + this.config = const GridCellDataConfig(), + }); @override Future loadData() { @@ -54,20 +56,17 @@ class CellDataLoader extends _GridCellDataLoader { }); }); } - - @override - GridCellDataConfig get config => _config; } -class DateCellDataLoader extends _GridCellDataLoader { +class DateCellDataLoader extends IGridCellDataLoader { final GridCell gridCell; - final GridCellDataConfig _config; + final IGridCellDataConfig _config; DateCellDataLoader({ required this.gridCell, - }) : _config = DefaultCellDataConfig(reloadOnFieldChanged: true); + }) : _config = const GridCellDataConfig(reloadOnFieldChanged: true); @override - GridCellDataConfig get config => _config; + IGridCellDataConfig get config => _config; @override Future loadData() { @@ -88,7 +87,7 @@ class DateCellDataLoader extends _GridCellDataLoader { } } -class SelectOptionCellDataLoader extends _GridCellDataLoader { +class SelectOptionCellDataLoader extends IGridCellDataLoader { final SelectOptionService service; final GridCell gridCell; SelectOptionCellDataLoader({ @@ -108,5 +107,5 @@ class SelectOptionCellDataLoader extends _GridCellDataLoader DefaultCellDataConfig(); + IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart index db84425f86..ba2c25fec6 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart @@ -1,40 +1,31 @@ -import 'dart:typed_data'; -import 'package:flowy_sdk/log.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'field_service.dart'; import 'package:dartz/dartz.dart'; -import 'package:protobuf/protobuf.dart'; - part 'field_editor_bloc.freezed.dart'; class FieldEditorBloc extends Bloc { - final String gridId; - final FieldContextLoader _loader; - FieldEditorBloc({ - required this.gridId, - required FieldContextLoader fieldLoader, - }) : _loader = fieldLoader, - super(FieldEditorState.initial(gridId)) { + required String gridId, + required String fieldName, + required FieldContextLoader fieldContextLoader, + }) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) { on( (event, emit) async { - await event.map( - initial: (_InitialField value) async { - await _getFieldTypeOptionContext(emit); + await event.when( + initial: () async { + final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader); + await fieldContext.loadData().then((result) { + result.fold( + (l) => emit(state.copyWith(fieldContext: Some(fieldContext))), + (r) => null, + ); + }); }, - updateName: (_UpdateName value) { - final newContext = _updateEditContext(name: value.name); - emit(state.copyWith(fieldTypeOptionData: newContext)); - }, - updateField: (_UpdateField value) { - final data = _updateEditContext(field: value.field, typeOptionData: value.typeOptionData); - emit(state.copyWith(fieldTypeOptionData: data)); - }, - done: (_Done value) async { - await _saveField(emit); + updateName: (name) { + state.fieldContext.fold(() => null, (fieldContext) => fieldContext.fieldName = name); + emit(state.copyWith(name: name)); }, ); }, @@ -45,78 +36,12 @@ class FieldEditorBloc extends Bloc { Future close() async { return super.close(); } - - Option _updateEditContext({ - String? name, - Field? field, - List? typeOptionData, - }) { - return state.fieldTypeOptionData.fold( - () => none(), - (context) { - context.freeze(); - final newFieldTypeOptionData = context.rebuild((newContext) { - newContext.field_2.rebuild((newField) { - if (name != null) { - newField.name = name; - } - - newContext.field_2 = newField; - }); - - if (field != null) { - newContext.field_2 = field; - } - - if (typeOptionData != null) { - newContext.typeOptionData = typeOptionData; - } - }); - - FieldService.insertField( - gridId: gridId, - field: newFieldTypeOptionData.field_2, - typeOptionData: newFieldTypeOptionData.typeOptionData, - ); - - return Some(newFieldTypeOptionData); - }, - ); - } - - Future _saveField(Emitter emit) async { - await state.fieldTypeOptionData.fold( - () async => null, - (data) async { - final result = await FieldService.insertField( - gridId: gridId, - field: data.field_2, - typeOptionData: data.typeOptionData, - ); - result.fold((l) => null, (r) => null); - }, - ); - } - - Future _getFieldTypeOptionContext(Emitter emit) async { - final result = await _loader.load(); - result.fold( - (context) { - emit(state.copyWith( - fieldTypeOptionData: Some(context), - )); - }, - (err) => Log.error(err), - ); - } } @freezed class FieldEditorEvent with _$FieldEditorEvent { const factory FieldEditorEvent.initial() = _InitialField; const factory FieldEditorEvent.updateName(String name) = _UpdateName; - const factory FieldEditorEvent.updateField(Field field, Uint8List typeOptionData) = _UpdateField; - const factory FieldEditorEvent.done() = _Done; } @freezed @@ -124,12 +49,14 @@ class FieldEditorState with _$FieldEditorState { const factory FieldEditorState({ required String gridId, required String errorText, - required Option fieldTypeOptionData, + required String name, + required Option fieldContext, }) = _FieldEditorState; - factory FieldEditorState.initial(String gridId) => FieldEditorState( + factory FieldEditorState.initial(String gridId, String fieldName, FieldContextLoader loader) => FieldEditorState( gridId: gridId, - fieldTypeOptionData: none(), + fieldContext: none(), errorText: '', + name: fieldName, ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart index ea10e4222f..c6d48c9d18 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart @@ -1,24 +1,29 @@ -import 'dart:typed_data'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; +import 'field_service.dart'; + part 'field_editor_pannel_bloc.freezed.dart'; class FieldEditorPannelBloc extends Bloc { - FieldEditorPannelBloc(FieldTypeOptionData editContext) : super(FieldEditorPannelState.initial(editContext)) { + final GridFieldContext _fieldContext; + void Function()? _fieldListenFn; + + FieldEditorPannelBloc(GridFieldContext fieldContext) + : _fieldContext = fieldContext, + super(FieldEditorPannelState.initial(fieldContext)) { on( (event, emit) async { - await event.map( - toFieldType: (_ToFieldType value) async { - emit(state.copyWith( - field: value.field, - typeOptionData: Uint8List.fromList(value.typeOptionData), - )); + event.when( + initial: () { + _fieldListenFn = fieldContext.addFieldListener((field) { + add(FieldEditorPannelEvent.didReceiveFieldUpdated(field)); + }); }, - didUpdateTypeOptionData: (_DidUpdateTypeOptionData value) { - emit(state.copyWith(typeOptionData: value.typeOptionData)); + didReceiveFieldUpdated: (field) { + emit(state.copyWith(field: field)); }, ); }, @@ -27,27 +32,26 @@ class FieldEditorPannelBloc extends Bloc close() async { + if (_fieldListenFn != null) { + _fieldContext.removeFieldListener(_fieldListenFn!); + } return super.close(); } } @freezed class FieldEditorPannelEvent with _$FieldEditorPannelEvent { - const factory FieldEditorPannelEvent.toFieldType(Field field, List typeOptionData) = _ToFieldType; - const factory FieldEditorPannelEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData; + const factory FieldEditorPannelEvent.initial() = _Initial; + const factory FieldEditorPannelEvent.didReceiveFieldUpdated(Field field) = _DidReceiveFieldUpdated; } @freezed class FieldEditorPannelState with _$FieldEditorPannelState { const factory FieldEditorPannelState({ - required String gridId, required Field field, - required Uint8List typeOptionData, }) = _FieldEditorPannelState; - factory FieldEditorPannelState.initial(FieldTypeOptionData data) => FieldEditorPannelState( - gridId: data.gridId, - field: data.field_2, - typeOptionData: Uint8List.fromList(data.typeOptionData), + factory FieldEditorPannelState.initial(GridFieldContext fieldContext) => FieldEditorPannelState( + field: fieldContext.field, ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart index a760f13176..03f27dffe7 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart @@ -1,9 +1,12 @@ import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; +import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; +import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:protobuf/protobuf.dart'; part 'field_service.freezed.dart'; class FieldService { @@ -199,3 +202,111 @@ class DefaultFieldContextLoader extends FieldContextLoader { return fieldService.switchToField(fieldType); } } + +class GridFieldContext { + final String gridId; + final FieldContextLoader _loader; + + late FieldTypeOptionData _data; + ValueNotifier? _fieldNotifier; + + GridFieldContext({ + required this.gridId, + required FieldContextLoader loader, + }) : _loader = loader; + + Future> loadData() async { + final result = await _loader.load(); + return result.fold( + (data) { + data.freeze(); + _data = data; + + if (_fieldNotifier == null) { + _fieldNotifier = ValueNotifier(data.field_2); + } else { + _fieldNotifier?.value = data.field_2; + } + + return left(unit); + }, + (err) { + Log.error(err); + return right(err); + }, + ); + } + + Field get field => _data.field_2; + + set field(Field field) { + _updateData(newField: field); + } + + List get typeOptionData => _data.typeOptionData; + + set fieldName(String name) { + _updateData(name: name); + } + + set typeOptionData(List typeOptionData) { + _updateData(typeOptionData: typeOptionData); + } + + void _updateData({String? name, Field? newField, List? typeOptionData}) { + _data = _data.rebuild((rebuildData) { + if (name != null) { + rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) { + rebuildField.name = name; + }); + } + + if (newField != null) { + rebuildData.field_2 = newField; + } + + if (typeOptionData != null) { + rebuildData.typeOptionData = typeOptionData; + } + }); + + if (_data.field_2 != _fieldNotifier?.value) { + _fieldNotifier?.value = _data.field_2; + } + + FieldService.insertField( + gridId: gridId, + field: field, + typeOptionData: typeOptionData, + ); + } + + Future switchToField(FieldType newFieldType) { + return _loader.switchToField(field.id, newFieldType).then((result) { + return result.fold( + (fieldTypeOptionData) { + _updateData( + newField: fieldTypeOptionData.field_2, + typeOptionData: fieldTypeOptionData.typeOptionData, + ); + }, + (err) { + Log.error(err); + }, + ); + }); + } + + void Function() addFieldListener(void Function(Field) callback) { + listener() { + callback(field); + } + + _fieldNotifier?.addListener(listener); + return listener; + } + + void removeFieldListener(void Function() listener) { + _fieldNotifier?.removeListener(listener); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart index c762f14c1d..cf9b69780d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart @@ -38,15 +38,17 @@ abstract class TypeOptionDataBuilder { } class TypeOptionContext { - final String gridId; - final Field field; - final Uint8List data; + final GridFieldContext _fieldContext; TypeOptionContext({ - required this.gridId, - required this.field, - required this.data, - }); + required GridFieldContext fieldContext, + }) : _fieldContext = fieldContext; + + String get gridId => _fieldContext.gridId; + + Field get field => _fieldContext.field; + + Uint8List get data => Uint8List.fromList(_fieldContext.typeOptionData); } abstract class TypeOptionFieldDelegate { 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 2effe120c2..14e1ee3d33 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 @@ -224,48 +224,46 @@ class _SelectOptionCell extends StatelessWidget { final theme = context.watch(); return SizedBox( height: GridSize.typeOptionItemHeight, - child: Stack( - fit: StackFit.expand, + child: Row( children: [ - _body(theme, context), - InkWell( - onTap: () { - context.read().add(SelectOptionEditorEvent.selectOption(option.id)); - }, - ), + Expanded(child: _body(theme, context)), + FlowyIconButton( + width: 30, + onPressed: () => _showEditPannel(context), + iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), + icon: svgWidget("editor/details", color: theme.iconColor), + ) ], ), ); } - FlowyHover _body(AppTheme theme, BuildContext context) { - return FlowyHover( - style: HoverStyle(hoverColor: theme.hover), - builder: (_, onHover) { - List children = [ - SelectOptionTag( - name: option.name, - color: option.color.make(context), - isSelected: isSelected, - ), - const Spacer(), - ]; - - if (isSelected) { - children.add(svgWidget("grid/checkmark")); - } - - if (onHover) { - children.add(FlowyIconButton( - width: 30, - onPressed: () => _showEditPannel(context), - iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), - icon: svgWidget("editor/details", color: theme.iconColor), - )); - } - - return Row(children: children); - }, + Widget _body(AppTheme theme, BuildContext context) { + return Stack( + fit: StackFit.expand, + children: [ + FlowyHover( + style: HoverStyle(hoverColor: theme.hover), + builder: (_, onHover) { + return InkWell( + child: Row(children: [ + const HSpace(6), + SelectOptionTag( + name: option.name, + color: option.color.make(context), + isSelected: isSelected, + ), + const Spacer(), + if (isSelected) svgWidget("grid/checkmark"), + const HSpace(6), + ]), + onTap: () { + context.read().add(SelectOptionEditorEvent.selectOption(option.id)); + }, + ); + }, + ), + ], ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 1e67f4c70b..9eec395ce0 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -63,6 +63,7 @@ class GridFieldCell extends StatelessWidget { FieldEditor( gridId: state.gridId, + fieldName: state.field.name, contextLoader: DefaultFieldContextLoader( gridId: state.gridId, field: state.field, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart index cc3a12930c..b3d96949f0 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart @@ -1,4 +1,3 @@ -import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/field/field_editor_bloc.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -11,16 +10,42 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'field_name_input.dart'; import 'field_editor_pannel.dart'; -class FieldEditor extends FlowyOverlayDelegate { +class FieldEditor extends StatelessWidget with FlowyOverlayDelegate { final String gridId; - final FieldEditorBloc _fieldEditorBloc; + final String fieldName; + final FieldContextLoader contextLoader; - FieldEditor({ + const FieldEditor({ required this.gridId, + required this.fieldName, required this.contextLoader, Key? key, - }) : _fieldEditorBloc = getIt(param1: gridId, param2: contextLoader) { - _fieldEditorBloc.add(const FieldEditorEvent.initial()); + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => FieldEditorBloc( + gridId: gridId, + fieldName: fieldName, + fieldContextLoader: contextLoader, + )..add(const FieldEditorEvent.initial()), + child: BlocBuilder( + buildWhen: (p, c) => false, + builder: (context, state) { + return ListView( + shrinkWrap: true, + children: [ + FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12), + const VSpace(10), + const _FieldNameTextField(), + const VSpace(10), + const _FieldPannel(), + ], + ); + }, + ), + ); } void show( @@ -28,10 +53,9 @@ class FieldEditor extends FlowyOverlayDelegate { AnchorDirection anchorDirection = AnchorDirection.bottomWithLeftAligned, }) { FlowyOverlay.of(context).remove(identifier()); - final child = _FieldEditorPage(_fieldEditorBloc, contextLoader); FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( - child: child, + child: this, constraints: BoxConstraints.loose(const Size(280, 400)), ), identifier: identifier(), @@ -46,49 +70,23 @@ class FieldEditor extends FlowyOverlayDelegate { return (FieldEditor).toString(); } - @override - void didRemove() { - _fieldEditorBloc.add(const FieldEditorEvent.done()); - } - @override bool asBarrier() => true; } -class _FieldEditorPage extends StatelessWidget { - final FieldEditorBloc editorBloc; - final FieldContextLoader contextLoader; - const _FieldEditorPage(this.editorBloc, this.contextLoader, {Key? key}) : super(key: key); +class _FieldPannel extends StatelessWidget { + const _FieldPannel({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return BlocProvider.value( - value: editorBloc, - child: BlocBuilder( - builder: (context, state) { - return state.fieldTypeOptionData.fold( - () => const SizedBox(), - (fieldTypeOptionContext) => ListView( - shrinkWrap: true, - children: [ - FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12), - const VSpace(10), - const _FieldNameTextField(), - const VSpace(10), - FieldEditorPannel( - fieldTypeOptionData: fieldTypeOptionContext, - onSwitchToField: (fieldId, fieldType) { - return contextLoader.switchToField(fieldId, fieldType); - }, - onUpdated: (field, typeOptionData) { - context.read().add(FieldEditorEvent.updateField(field, typeOptionData)); - }, - ), - ], - ), - ); - }, - ), + return BlocBuilder( + buildWhen: (p, c) => p.fieldContext != c.fieldContext, + builder: (context, state) { + return state.fieldContext.fold( + () => const SizedBox(), + (fieldContext) => FieldEditorPannel(fieldContext: fieldContext), + ); + }, ); } } @@ -98,16 +96,11 @@ class _FieldNameTextField extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocSelector( - selector: (state) { - return state.fieldTypeOptionData.fold( - () => "", - (fieldTypeOptionContext) => fieldTypeOptionContext.field_2.name, - ); - }, - builder: (context, name) { + return BlocBuilder( + buildWhen: (p, c) => p.name != c.name, + builder: (context, state) { return FieldNameTextField( - name: name, + name: state.name, errorText: context.read().state.errorText, onNameChanged: (newName) { context.read().add(FieldEditorEvent.updateName(newName)); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart index 5d96f92655..21df621705 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart @@ -7,14 +7,12 @@ import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pbserver.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart'; @@ -31,14 +29,10 @@ typedef SwitchToFieldCallback = Future> ); class FieldEditorPannel extends StatefulWidget { - final FieldTypeOptionData fieldTypeOptionData; - final UpdateFieldCallback onUpdated; - final SwitchToFieldCallback onSwitchToField; + final GridFieldContext fieldContext; const FieldEditorPannel({ - required this.fieldTypeOptionData, - required this.onUpdated, - required this.onSwitchToField, + required this.fieldContext, Key? key, }) : super(key: key); @@ -52,13 +46,10 @@ class _FieldEditorPannelState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => getIt(param1: widget.fieldTypeOptionData), - child: BlocConsumer( - listener: (context, state) { - widget.onUpdated(state.field, state.typeOptionData); - }, + create: (context) => FieldEditorPannelBloc(widget.fieldContext)..add(const FieldEditorPannelEvent.initial()), + child: BlocBuilder( builder: (context, state) { - List children = [_switchFieldTypeButton(context, state.field)]; + List children = [_switchFieldTypeButton(context, widget.fieldContext.field)]; final typeOptionWidget = _typeOptionWidget(context: context, state: state); if (typeOptionWidget != null) { @@ -84,19 +75,7 @@ class _FieldEditorPannelState extends State { hoverColor: theme.hover, onTap: () { final list = FieldTypeList(onSelectField: (newFieldType) { - widget.onSwitchToField(field.id, newFieldType).then((result) { - result.fold( - (fieldTypeOptionContext) { - context.read().add( - FieldEditorPannelEvent.toFieldType( - fieldTypeOptionContext.field_2, - fieldTypeOptionContext.typeOptionData, - ), - ); - }, - (err) => Log.error(err), - ); - }); + widget.fieldContext.switchToField(newFieldType); }); _showOverlay(context, list); }, @@ -116,15 +95,11 @@ class _FieldEditorPannelState extends State { ); final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) { - context.read().add(FieldEditorPannelEvent.didUpdateTypeOptionData(data)); + widget.fieldContext.typeOptionData = data; }); final builder = _makeTypeOptionBuild( - typeOptionContext: TypeOptionContext( - gridId: state.gridId, - field: state.field, - data: state.typeOptionData, - ), + typeOptionContext: TypeOptionContext(fieldContext: widget.fieldContext), overlayDelegate: overlayDelegate, dataDelegate: dataDelegate, ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart index d7b24b4d8c..5e1296191b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart @@ -150,6 +150,7 @@ class CreateFieldButton extends StatelessWidget { hoverColor: theme.hover, onTap: () => FieldEditor( gridId: gridId, + fieldName: "", contextLoader: NewFieldContextLoader(gridId: gridId), ).show(context), leftIcon: svgWidget("home/add"), 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 474a21773d..c8672d4ae3 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 @@ -178,6 +178,7 @@ class _RowDetailCell extends StatelessWidget { void _showFieldEditor(BuildContext context) { FieldEditor( gridId: gridCell.gridId, + fieldName: gridCell.field.name, contextLoader: DefaultFieldContextLoader( gridId: gridCell.gridId, field: gridCell.field, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart index 151afc1e3f..ec5ca40b1e 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart @@ -115,6 +115,7 @@ class _GridPropertyCell extends StatelessWidget { onTap: () { FieldEditor( gridId: gridId, + fieldName: field.name, contextLoader: DefaultFieldContextLoader(gridId: gridId, field: field), ).show(context, anchorDirection: AnchorDirection.bottomRight); }, 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 5189908192..4f06a40b9b 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 @@ -109,7 +109,7 @@ abstract class HoverWidget extends StatefulWidget { } class FlowyHover2 extends StatefulWidget { - final HoverWidget child; + final Widget child; final EdgeInsets contentPadding; const FlowyHover2({ required this.child, @@ -127,9 +127,14 @@ class _FlowyHover2State extends State { @override void initState() { _hoverState = FlowyHoverState(); - widget.child.onFocus.addListener(() { - _hoverState.onFocus = widget.child.onFocus.value; - }); + + if (widget.child is HoverWidget) { + final hoverWidget = widget.child as HoverWidget; + hoverWidget.onFocus.addListener(() { + _hoverState.onFocus = hoverWidget.onFocus.value; + }); + } + super.initState(); } 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 ccbaff82e9..06b4f7222f 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -212,7 +212,7 @@ pub struct FieldTypeOptionData { pub grid_id: String, #[pb(index = 2)] - pub field_id: String, + pub field: Field, #[pb(index = 3)] pub type_option_data: Vec, diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index c0c03f65a7..6df3c847f0 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -2174,7 +2174,7 @@ impl ::protobuf::reflect::ProtobufValue for FieldTypeOptionContext { pub struct FieldTypeOptionData { // message fields pub grid_id: ::std::string::String, - pub field_id: ::std::string::String, + pub field: ::protobuf::SingularPtrField, pub type_option_data: ::std::vec::Vec, // special fields pub unknown_fields: ::protobuf::UnknownFields, @@ -2218,30 +2218,37 @@ impl FieldTypeOptionData { ::std::mem::replace(&mut self.grid_id, ::std::string::String::new()) } - // string field_id = 2; + // .Field field = 2; - pub fn get_field_id(&self) -> &str { - &self.field_id + pub fn get_field(&self) -> &Field { + self.field.as_ref().unwrap_or_else(|| ::default_instance()) } - pub fn clear_field_id(&mut self) { - self.field_id.clear(); + pub fn clear_field(&mut self) { + self.field.clear(); + } + + pub fn has_field(&self) -> bool { + self.field.is_some() } // Param is passed by value, moved - pub fn set_field_id(&mut self, v: ::std::string::String) { - self.field_id = v; + pub fn set_field(&mut self, v: Field) { + self.field = ::protobuf::SingularPtrField::some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_field_id(&mut self) -> &mut ::std::string::String { - &mut self.field_id + pub fn mut_field(&mut self) -> &mut Field { + if self.field.is_none() { + self.field.set_default(); + } + self.field.as_mut().unwrap() } // Take field - pub fn take_field_id(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) + pub fn take_field(&mut self) -> Field { + self.field.take().unwrap_or_else(|| Field::new()) } // bytes type_option_data = 3; @@ -2273,6 +2280,11 @@ impl FieldTypeOptionData { impl ::protobuf::Message for FieldTypeOptionData { fn is_initialized(&self) -> bool { + for v in &self.field { + if !v.is_initialized() { + return false; + } + }; true } @@ -2284,7 +2296,7 @@ impl ::protobuf::Message for FieldTypeOptionData { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?; }, 2 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; + ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.field)?; }, 3 => { ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.type_option_data)?; @@ -2304,8 +2316,9 @@ impl ::protobuf::Message for FieldTypeOptionData { if !self.grid_id.is_empty() { my_size += ::protobuf::rt::string_size(1, &self.grid_id); } - if !self.field_id.is_empty() { - my_size += ::protobuf::rt::string_size(2, &self.field_id); + if let Some(ref v) = self.field.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; } if !self.type_option_data.is_empty() { my_size += ::protobuf::rt::bytes_size(3, &self.type_option_data); @@ -2319,8 +2332,10 @@ impl ::protobuf::Message for FieldTypeOptionData { if !self.grid_id.is_empty() { os.write_string(1, &self.grid_id)?; } - if !self.field_id.is_empty() { - os.write_string(2, &self.field_id)?; + if let Some(ref v) = self.field.as_ref() { + os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?; + os.write_raw_varint32(v.get_cached_size())?; + v.write_to_with_cached_sizes(os)?; } if !self.type_option_data.is_empty() { os.write_bytes(3, &self.type_option_data)?; @@ -2368,10 +2383,10 @@ impl ::protobuf::Message for FieldTypeOptionData { |m: &FieldTypeOptionData| { &m.grid_id }, |m: &mut FieldTypeOptionData| { &mut m.grid_id }, )); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "field_id", - |m: &FieldTypeOptionData| { &m.field_id }, - |m: &mut FieldTypeOptionData| { &mut m.field_id }, + fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + "field", + |m: &FieldTypeOptionData| { &m.field }, + |m: &mut FieldTypeOptionData| { &mut m.field }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( "type_option_data", @@ -2395,7 +2410,7 @@ impl ::protobuf::Message for FieldTypeOptionData { impl ::protobuf::Clear for FieldTypeOptionData { fn clear(&mut self) { self.grid_id.clear(); - self.field_id.clear(); + self.field.clear(); self.type_option_data.clear(); self.unknown_fields.clear(); } @@ -8302,25 +8317,25 @@ static file_descriptor_proto_data: &'static [u8] = b"\ ne_of_field_id\"\x82\x01\n\x16FieldTypeOptionContext\x12\x17\n\x07grid_i\ d\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\ \x06.FieldR\tgridField\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\ - \x0etypeOptionData\"s\n\x13FieldTypeOptionData\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07\ - fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionDa\ - ta\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.Fiel\ - dR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\ - \x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\ - \x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07b\ - lockId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\"\xb8\x01\n\ - \x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_fiel\ - d_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFieldId\ - \x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellByFie\ - ldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\ - \x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\ - \x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Repe\ - atedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05i\ - tems\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\ - \x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrd\ - ers\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrd\ - erR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\ + \x0etypeOptionData\"v\n\x13FieldTypeOptionData\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.\ + FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etype\ + OptionData\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\ + \x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\ + \x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06ro\ + w_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\ + \tR\x07blockId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\"\xb8\ + \x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_b\ + y_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFiel\ + dId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellBy\ + FieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05va\ + lue\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedR\ + ow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Re\ + peatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\ + \x05items\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\ + \tR\x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ + Orders\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.Row\ + OrderR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\ \x0e\n\x0cone_of_index\"Q\n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\ \x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\ \x0b2\x04.RowR\x03row\"\xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08bloc\ diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index c1aa74b0e7..99024d3841 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -45,7 +45,7 @@ message FieldTypeOptionContext { } message FieldTypeOptionData { string grid_id = 1; - string field_id = 2; + Field field = 2; bytes type_option_data = 3; } message RepeatedField { From 5f5177355f16f30ebe983eee879305d5b88c3e35 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 18 May 2022 16:14:11 +0800 Subject: [PATCH 04/82] chore: update _OptionList cell UI --- .../cell/select_option_cell/extension.dart | 39 +++++++++++++++++++ .../select_option_editor.dart | 29 +++----------- .../header/type_option/select_option.dart | 17 +++++--- 3 files changed, 57 insertions(+), 28 deletions(-) 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 c4140ad216..87042a419f 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 @@ -1,5 +1,8 @@ +import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -106,3 +109,39 @@ class SelectOptionTag extends StatelessWidget { // ); } } + +class SelectOptionTagCell extends StatelessWidget { + final List children; + final void Function(SelectOption) onSelected; + final SelectOption option; + const SelectOptionTagCell({ + required this.option, + required this.onSelected, + this.children = const [], + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return Stack( + fit: StackFit.expand, + children: [ + FlowyHover( + style: HoverStyle(hoverColor: theme.hover), + child: InkWell( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 3), + child: Row(children: [ + 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 14e1ee3d33..e504e48f33 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 @@ -239,30 +239,13 @@ class _SelectOptionCell extends StatelessWidget { } Widget _body(AppTheme theme, BuildContext context) { - return Stack( - fit: StackFit.expand, + return SelectOptionTagCell( + option: option, + onSelected: (option) { + context.read().add(SelectOptionEditorEvent.selectOption(option.id)); + }, children: [ - FlowyHover( - style: HoverStyle(hoverColor: theme.hover), - builder: (_, onHover) { - return InkWell( - child: Row(children: [ - const HSpace(6), - SelectOptionTag( - name: option.name, - color: option.color.make(context), - isSelected: isSelected, - ), - const Spacer(), - if (isSelected) svgWidget("grid/checkmark"), - const HSpace(6), - ]), - onTap: () { - context.read().add(SelectOptionEditorEvent.selectOption(option.id)); - }, - ); - }, - ), + if (isSelected) svgWidget("grid/checkmark"), ], ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart index a6a76673b3..4b6b1da614 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart @@ -1,10 +1,12 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/extension.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; @@ -183,13 +185,18 @@ class _OptionCell extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); + return SizedBox( height: GridSize.typeOptionItemHeight, - child: FlowyButton( - text: FlowyText.medium(option.name, fontSize: 12), - hoverColor: theme.hover, - onTap: () => onSelected(option), - rightIcon: svgWidget("grid/details", color: theme.iconColor), + child: SelectOptionTagCell( + option: option, + onSelected: onSelected, + children: [ + svgWidget( + "grid/details", + color: theme.iconColor, + ), + ], ), ); } From 1267c7524eaee2c3c9fd086b7043f6ce8a639354 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 18 May 2022 17:26:50 +0800 Subject: [PATCH 05/82] refactor: TypeOptionContext --- .../app_flowy/lib/startup/deps_resolver.dart | 10 -- .../grid/field/type_option/date_bloc.dart | 13 ++- .../field/type_option/multi_select_bloc.dart | 18 ++- .../grid/field/type_option/number_bloc.dart | 13 ++- .../field/type_option/single_select_bloc.dart | 15 ++- .../type_option/type_option_service.dart | 21 +++- .../cell/select_option_cell/extension.dart | 2 - .../select_option_editor.dart | 1 - .../widgets/header/field_editor_pannel.dart | 104 +++++++++++------- .../widgets/header/type_option/checkbox.dart | 20 ++++ .../src/widgets/header/type_option/date.dart | 18 ++- .../header/type_option/multi_select.dart | 20 ++-- .../widgets/header/type_option/number.dart | 22 ++-- .../widgets/header/type_option/rich_text.dart | 21 ++++ .../header/type_option/select_option.dart | 7 +- .../header/type_option/single_select.dart | 20 ++-- 16 files changed, 211 insertions(+), 114 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 698cc63531..9be9420fb5 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -15,8 +15,6 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/menu/menu.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; import 'package:get_it/get_it.dart'; @@ -187,14 +185,6 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( - (typeOption, _) => DateTypeOptionBloc(typeOption: typeOption), - ); - - getIt.registerFactoryParam( - (typeOption, _) => NumberTypeOptionBloc(typeOption: typeOption), - ); - getIt.registerFactoryParam( (gridId, cache) => GridPropertyBloc(gridId: gridId, fieldCache: cache), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart index c0a232a84f..8784422c54 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.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'; @@ -5,8 +6,18 @@ import 'dart:async'; import 'package:protobuf/protobuf.dart'; part 'date_bloc.freezed.dart'; +typedef DateTypeOptionContext = TypeOptionContext; + +class DateTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + DateTypeOption fromBuffer(List buffer) { + return DateTypeOption.fromBuffer(buffer); + } +} + class DateTypeOptionBloc extends Bloc { - DateTypeOptionBloc({required DateTypeOption typeOption}) : super(DateTypeOptionState.initial(typeOption)) { + DateTypeOptionBloc({required DateTypeOptionContext typeOptionContext}) + : super(DateTypeOptionState.initial(typeOptionContext.typeOption)) { on( (event, emit) async { event.map( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart index 0d42612595..f755abc2ce 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart @@ -8,12 +8,24 @@ import 'type_option_service.dart'; part 'multi_select_bloc.freezed.dart'; +typedef MultiSelectTypeOptionContext = TypeOptionContext; + +class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + MultiSelectTypeOption fromBuffer(List buffer) { + return MultiSelectTypeOption.fromBuffer(buffer); + } +} + class MultiSelectTypeOptionBloc extends Bloc { final TypeOptionService service; - MultiSelectTypeOptionBloc(TypeOptionContext typeOptionContext) - : service = TypeOptionService(gridId: typeOptionContext.gridId, fieldId: typeOptionContext.field.id), - super(MultiSelectTypeOptionState.initial(MultiSelectTypeOption.fromBuffer(typeOptionContext.data))) { + MultiSelectTypeOptionBloc(MultiSelectTypeOptionContext typeOptionContext) + : service = TypeOptionService( + gridId: typeOptionContext.gridId, + fieldId: typeOptionContext.field.id, + ), + super(MultiSelectTypeOptionState.initial(typeOptionContext.typeOption)) { on( (event, emit) async { await event.map( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart index a655ebc1e8..951bdb9261 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -6,8 +7,18 @@ import 'package:protobuf/protobuf.dart'; part 'number_bloc.freezed.dart'; +typedef NumberTypeOptionContext = TypeOptionContext; + +class NumberTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + NumberTypeOption fromBuffer(List buffer) { + return NumberTypeOption.fromBuffer(buffer); + } +} + class NumberTypeOptionBloc extends Bloc { - NumberTypeOptionBloc({required NumberTypeOption typeOption}) : super(NumberTypeOptionState.initial(typeOption)) { + NumberTypeOptionBloc({required NumberTypeOptionContext typeOptionContext}) + : super(NumberTypeOptionState.initial(typeOptionContext.typeOption)) { on( (event, emit) async { event.map( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart index 36a2f956b3..624e8fe08e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart @@ -8,15 +8,22 @@ import 'type_option_service.dart'; part 'single_select_bloc.freezed.dart'; +typedef SingleSelectTypeOptionContext = TypeOptionContext; + +class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + SingleSelectTypeOption fromBuffer(List buffer) { + return SingleSelectTypeOption.fromBuffer(buffer); + } +} + class SingleSelectTypeOptionBloc extends Bloc { final TypeOptionService service; SingleSelectTypeOptionBloc( - TypeOptionContext typeOptionContext, + SingleSelectTypeOptionContext typeOptionContext, ) : service = TypeOptionService(gridId: typeOptionContext.gridId, fieldId: typeOptionContext.field.id), - super( - SingleSelectTypeOptionState.initial(SingleSelectTypeOption.fromBuffer(typeOptionContext.data)), - ) { + super(SingleSelectTypeOptionState.initial(typeOptionContext.typeOption)) { on( (event, emit) async { await event.map( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart index cf9b69780d..c088618825 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart @@ -8,6 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'package:protobuf/protobuf.dart'; class TypeOptionService { final String gridId; @@ -37,10 +38,13 @@ abstract class TypeOptionDataBuilder { T fromBuffer(List buffer); } -class TypeOptionContext { +class TypeOptionContext { + T? _typeOptionObject; final GridFieldContext _fieldContext; + final TypeOptionDataBuilder dataBuilder; TypeOptionContext({ + required this.dataBuilder, required GridFieldContext fieldContext, }) : _fieldContext = fieldContext; @@ -48,7 +52,20 @@ class TypeOptionContext { Field get field => _fieldContext.field; - Uint8List get data => Uint8List.fromList(_fieldContext.typeOptionData); + T get typeOption { + if (_typeOptionObject != null) { + return _typeOptionObject!; + } + + final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData); + _typeOptionObject = object; + return object; + } + + set typeOption(T typeOption) { + _fieldContext.typeOptionData = typeOption.writeToBuffer(); + _typeOptionObject = null; + } } abstract class TypeOptionFieldDelegate { 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 87042a419f..3cbf2331fd 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 @@ -1,8 +1,6 @@ -import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; 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 e504e48f33..f82d52cfb4 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 @@ -7,7 +7,6 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart index 21df621705..30599cef30 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart @@ -1,6 +1,8 @@ import 'dart:typed_data'; +import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart'; import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart'; import 'package:dartz/dartz.dart' show Either; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -9,8 +11,6 @@ import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pbserver.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; @@ -20,6 +20,7 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header import 'field_type_extension.dart'; import 'type_option/multi_select.dart'; import 'type_option/number.dart'; +import 'type_option/rich_text.dart'; import 'type_option/single_select.dart'; typedef UpdateFieldCallback = void Function(Field, Uint8List); @@ -94,14 +95,9 @@ class _FieldEditorPannelState extends State { hideOverlay: _hideOverlay, ); - final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) { - widget.fieldContext.typeOptionData = data; - }); - final builder = _makeTypeOptionBuild( - typeOptionContext: TypeOptionContext(fieldContext: widget.fieldContext), + typeOptionContext: _makeTypeOptionContext(widget.fieldContext), overlayDelegate: overlayDelegate, - dataDelegate: dataDelegate, ); return builder.customWidget; @@ -141,27 +137,79 @@ abstract class TypeOptionBuilder { TypeOptionBuilder _makeTypeOptionBuild({ required TypeOptionContext typeOptionContext, required TypeOptionOverlayDelegate overlayDelegate, - required TypeOptionDataDelegate dataDelegate, }) { switch (typeOptionContext.field.fieldType) { case FieldType.Checkbox: - return CheckboxTypeOptionBuilder(typeOptionContext.data); + return CheckboxTypeOptionBuilder( + typeOptionContext as CheckboxTypeOptionContext, + ); case FieldType.DateTime: - return DateTypeOptionBuilder(typeOptionContext.data, overlayDelegate, dataDelegate); + return DateTypeOptionBuilder( + typeOptionContext as DateTypeOptionContext, + overlayDelegate, + ); case FieldType.SingleSelect: - return SingleSelectTypeOptionBuilder(typeOptionContext, overlayDelegate, dataDelegate); + return SingleSelectTypeOptionBuilder( + typeOptionContext as SingleSelectTypeOptionContext, + overlayDelegate, + ); case FieldType.MultiSelect: - return MultiSelectTypeOptionBuilder(typeOptionContext, overlayDelegate, dataDelegate); + return MultiSelectTypeOptionBuilder( + typeOptionContext as MultiSelectTypeOptionContext, + overlayDelegate, + ); case FieldType.Number: - return NumberTypeOptionBuilder(typeOptionContext.data, overlayDelegate, dataDelegate); + return NumberTypeOptionBuilder( + typeOptionContext as NumberTypeOptionContext, + overlayDelegate, + ); case FieldType.RichText: - return RichTextTypeOptionBuilder(typeOptionContext.data); + return RichTextTypeOptionBuilder( + typeOptionContext as RichTextTypeOptionContext, + ); default: throw UnimplementedError; } } +TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) { + switch (fieldContext.field.fieldType) { + case FieldType.Checkbox: + return CheckboxTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: CheckboxTypeOptionDataBuilder(), + ); + case FieldType.DateTime: + return DateTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: DateTypeOptionDataBuilder(), + ); + case FieldType.MultiSelect: + return MultiSelectTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: MultiSelectTypeOptionDataBuilder(), + ); + case FieldType.Number: + return NumberTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: NumberTypeOptionDataBuilder(), + ); + case FieldType.RichText: + return RichTextTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: RichTextTypeOptionDataBuilder(), + ); + case FieldType.SingleSelect: + return SingleSelectTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: SingleSelectTypeOptionDataBuilder(), + ); + default: + throw UnimplementedError(); + } +} + abstract class TypeOptionWidget extends StatelessWidget { const TypeOptionWidget({Key? key}) : super(key: key); } @@ -183,29 +231,3 @@ class TypeOptionOverlayDelegate { required this.hideOverlay, }); } - -class TypeOptionDataDelegate { - TypeOptionDataCallback didUpdateTypeOptionData; - - TypeOptionDataDelegate({ - required this.didUpdateTypeOptionData, - }); -} - -class RichTextTypeOptionBuilder extends TypeOptionBuilder { - RichTextTypeOption typeOption; - - RichTextTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = RichTextTypeOption.fromBuffer(typeOptionData); - - @override - Widget? get customWidget => null; -} - -class CheckboxTypeOptionBuilder extends TypeOptionBuilder { - CheckboxTypeOption typeOption; - - CheckboxTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = CheckboxTypeOption.fromBuffer(typeOptionData); - - @override - Widget? get customWidget => null; -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart new file mode 100644 index 0000000000..c1e202f358 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart @@ -0,0 +1,20 @@ +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart'; +import 'package:flutter/material.dart'; + +typedef CheckboxTypeOptionContext = TypeOptionContext; + +class CheckboxTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + CheckboxTypeOption fromBuffer(List buffer) { + return CheckboxTypeOption.fromBuffer(buffer); + } +} + +class CheckboxTypeOptionBuilder extends TypeOptionBuilder { + CheckboxTypeOptionBuilder(CheckboxTypeOptionContext typeOptionContext); + + @override + Widget? get customWidget => null; +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart index 677e043703..f4031dc9ce 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart @@ -1,4 +1,3 @@ -import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/field/type_option/date_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; @@ -18,12 +17,10 @@ class DateTypeOptionBuilder extends TypeOptionBuilder { final DateTypeOptionWidget _widget; DateTypeOptionBuilder( - TypeOptionData typeOptionData, + DateTypeOptionContext typeOptionContext, TypeOptionOverlayDelegate overlayDelegate, - TypeOptionDataDelegate dataDelegate, ) : _widget = DateTypeOptionWidget( - typeOption: DateTypeOption.fromBuffer(typeOptionData), - dataDelegate: dataDelegate, + typeOptionContext: typeOptionContext, overlayDelegate: overlayDelegate, ); @@ -32,12 +29,11 @@ class DateTypeOptionBuilder extends TypeOptionBuilder { } class DateTypeOptionWidget extends TypeOptionWidget { - final DateTypeOption typeOption; + final DateTypeOptionContext typeOptionContext; final TypeOptionOverlayDelegate overlayDelegate; - final TypeOptionDataDelegate dataDelegate; + const DateTypeOptionWidget({ - required this.typeOption, - required this.dataDelegate, + required this.typeOptionContext, required this.overlayDelegate, Key? key, }) : super(key: key); @@ -45,9 +41,9 @@ class DateTypeOptionWidget extends TypeOptionWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => getIt(param1: typeOption), + create: (context) => DateTypeOptionBloc(typeOptionContext: typeOptionContext), child: BlocConsumer( - listener: (context, state) => dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()), + listener: (context, state) => typeOptionContext.typeOption = state.typeOption, builder: (context, state) { return Column(children: [ _renderDateFormatButton(context, state.typeOption.dateFormat), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart index cd2997738c..b1b530fcf3 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart @@ -1,5 +1,4 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart'; -import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,13 +9,11 @@ class MultiSelectTypeOptionBuilder extends TypeOptionBuilder { final MultiSelectTypeOptionWidget _widget; MultiSelectTypeOptionBuilder( - TypeOptionContext typeOptionContext, + MultiSelectTypeOptionContext typeOptionContext, TypeOptionOverlayDelegate overlayDelegate, - TypeOptionDataDelegate dataDelegate, ) : _widget = MultiSelectTypeOptionWidget( typeOptionContext: typeOptionContext, overlayDelegate: overlayDelegate, - dataDelegate: dataDelegate, ); @override @@ -24,13 +21,12 @@ class MultiSelectTypeOptionBuilder extends TypeOptionBuilder { } class MultiSelectTypeOptionWidget extends TypeOptionWidget { - final TypeOptionContext typeOptionContext; + final MultiSelectTypeOptionContext typeOptionContext; final TypeOptionOverlayDelegate overlayDelegate; - final TypeOptionDataDelegate dataDelegate; + const MultiSelectTypeOptionWidget({ required this.typeOptionContext, required this.overlayDelegate, - required this.dataDelegate, Key? key, }) : super(key: key); @@ -40,7 +36,7 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { create: (context) => MultiSelectTypeOptionBloc(typeOptionContext), child: BlocConsumer( listener: (context, state) { - dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()); + typeOptionContext.typeOption = state.typeOption; }, builder: (context, state) { return SelectOptionTypeOptionWidget( @@ -51,11 +47,11 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { createSelectOptionCallback: (name) { context.read().add(MultiSelectTypeOptionEvent.createOption(name)); }, - updateSelectOptionCallback: (updateOption) { - context.read().add(MultiSelectTypeOptionEvent.updateOption(updateOption)); + updateSelectOptionCallback: (option) { + context.read().add(MultiSelectTypeOptionEvent.updateOption(option)); }, - deleteSelectOptionCallback: (deleteOption) { - context.read().add(MultiSelectTypeOptionEvent.deleteOption(deleteOption)); + deleteSelectOptionCallback: (option) { + context.read().add(MultiSelectTypeOptionEvent.deleteOption(option)); }, overlayDelegate: overlayDelegate, // key: ValueKey(state.typeOption.hashCode), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart index 9e3ced1c43..3a330a02b7 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart @@ -1,4 +1,3 @@ -import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/field/type_option/number_bloc.dart'; import 'package:app_flowy/workspace/application/grid/field/type_option/number_format_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; @@ -20,12 +19,10 @@ class NumberTypeOptionBuilder extends TypeOptionBuilder { final NumberTypeOptionWidget _widget; NumberTypeOptionBuilder( - TypeOptionData typeOptionData, + NumberTypeOptionContext typeOptionContext, TypeOptionOverlayDelegate overlayDelegate, - TypeOptionDataDelegate dataDelegate, ) : _widget = NumberTypeOptionWidget( - typeOption: NumberTypeOption.fromBuffer(typeOptionData), - dataDelegate: dataDelegate, + typeOptionContext: typeOptionContext, overlayDelegate: overlayDelegate, ); @@ -34,22 +31,23 @@ class NumberTypeOptionBuilder extends TypeOptionBuilder { } class NumberTypeOptionWidget extends TypeOptionWidget { - final TypeOptionDataDelegate dataDelegate; final TypeOptionOverlayDelegate overlayDelegate; - final NumberTypeOption typeOption; - const NumberTypeOptionWidget( - {required this.typeOption, required this.dataDelegate, required this.overlayDelegate, Key? key}) - : super(key: key); + final NumberTypeOptionContext typeOptionContext; + const NumberTypeOptionWidget({ + required this.typeOptionContext, + required this.overlayDelegate, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); return BlocProvider( - create: (context) => getIt(param1: typeOption), + create: (context) => NumberTypeOptionBloc(typeOptionContext: typeOptionContext), child: SizedBox( height: GridSize.typeOptionItemHeight, child: BlocConsumer( - listener: (context, state) => dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()), + listener: (context, state) => typeOptionContext.typeOption = state.typeOption, builder: (context, state) { return FlowyButton( text: Row( diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart new file mode 100644 index 0000000000..03f9ed347c --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/rich_text.dart @@ -0,0 +1,21 @@ +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart'; + +import 'package:flutter/material.dart'; + +typedef RichTextTypeOptionContext = TypeOptionContext; + +class RichTextTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + RichTextTypeOption fromBuffer(List buffer) { + return RichTextTypeOption.fromBuffer(buffer); + } +} + +class RichTextTypeOptionBuilder extends TypeOptionBuilder { + RichTextTypeOptionBuilder(RichTextTypeOptionContext typeOptionContext); + + @override + Widget? get customWidget => null; +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart index 4b6b1da614..68711a0ad4 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart @@ -6,7 +6,6 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; @@ -63,7 +62,11 @@ class SelectOptionTypeOptionWidget extends StatelessWidget { List children = [ const TypeOptionSeparator(), const OptionTitle(), - if (state.isEditingOption) const _CreateOptionTextField(), + if (state.isEditingOption) + const Padding( + padding: EdgeInsets.only(bottom: 10), + child: _CreateOptionTextField(), + ), if (state.options.isEmpty && !state.isEditingOption) const _AddOptionButton(), _OptionList(overlayDelegate) ]; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart index 11793fbb43..bbe3b1550b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart @@ -1,5 +1,4 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_bloc.dart'; -import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -9,12 +8,10 @@ class SingleSelectTypeOptionBuilder extends TypeOptionBuilder { final SingleSelectTypeOptionWidget _widget; SingleSelectTypeOptionBuilder( - TypeOptionContext typeOptionContext, + SingleSelectTypeOptionContext typeOptionContext, TypeOptionOverlayDelegate overlayDelegate, - TypeOptionDataDelegate dataDelegate, ) : _widget = SingleSelectTypeOptionWidget( typeOptionContext: typeOptionContext, - dataDelegate: dataDelegate, overlayDelegate: overlayDelegate, ); @@ -23,12 +20,11 @@ class SingleSelectTypeOptionBuilder extends TypeOptionBuilder { } class SingleSelectTypeOptionWidget extends TypeOptionWidget { - final TypeOptionContext typeOptionContext; + final SingleSelectTypeOptionContext typeOptionContext; final TypeOptionOverlayDelegate overlayDelegate; - final TypeOptionDataDelegate dataDelegate; + const SingleSelectTypeOptionWidget({ required this.typeOptionContext, - required this.dataDelegate, required this.overlayDelegate, Key? key, }) : super(key: key); @@ -39,7 +35,7 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget { create: (context) => SingleSelectTypeOptionBloc(typeOptionContext), child: BlocConsumer( listener: (context, state) { - dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()); + typeOptionContext.typeOption = state.typeOption; }, builder: (context, state) { return SelectOptionTypeOptionWidget( @@ -50,11 +46,11 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget { createSelectOptionCallback: (name) { context.read().add(SingleSelectTypeOptionEvent.createOption(name)); }, - updateSelectOptionCallback: (updateOption) { - context.read().add(SingleSelectTypeOptionEvent.updateOption(updateOption)); + updateSelectOptionCallback: (option) { + context.read().add(SingleSelectTypeOptionEvent.updateOption(option)); }, - deleteSelectOptionCallback: (deleteOption) { - context.read().add(SingleSelectTypeOptionEvent.deleteOption(deleteOption)); + deleteSelectOptionCallback: (option) { + context.read().add(SingleSelectTypeOptionEvent.deleteOption(option)); }, overlayDelegate: overlayDelegate, // key: ValueKey(state.typeOption.hashCode), From f3b87d419fe9cf5d18bb8777b3df1004e0e925e8 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 18 May 2022 20:11:49 +0800 Subject: [PATCH 06/82] chore: optimaze create option progress --- .../field/type_option/multi_select_bloc.dart | 101 ------------------ .../type_option/multi_select_type_option.dart | 77 +++++++++++++ .../select_option_type_option_bloc.dart | 49 +++++---- .../field/type_option/single_select_bloc.dart | 99 ----------------- .../single_select_type_option.dart | 78 ++++++++++++++ .../type_option/type_option_service.dart | 2 +- .../workspace/application/grid/prelude.dart | 2 +- .../grid/src/widgets/common/text_field.dart | 6 ++ .../widgets/header/field_editor_pannel.dart | 2 +- .../header/type_option/multi_select.dart | 35 ++---- .../header/type_option/select_option.dart | 55 ++++------ .../header/type_option/single_select.dart | 35 ++---- 12 files changed, 227 insertions(+), 314 deletions(-) delete mode 100644 frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart create mode 100644 frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart delete mode 100644 frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart create mode 100644 frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart deleted file mode 100644 index f755abc2ce..0000000000 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flowy_sdk/log.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'dart:async'; -import 'package:protobuf/protobuf.dart'; -import 'type_option_service.dart'; - -part 'multi_select_bloc.freezed.dart'; - -typedef MultiSelectTypeOptionContext = TypeOptionContext; - -class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { - @override - MultiSelectTypeOption fromBuffer(List buffer) { - return MultiSelectTypeOption.fromBuffer(buffer); - } -} - -class MultiSelectTypeOptionBloc extends Bloc { - final TypeOptionService service; - - MultiSelectTypeOptionBloc(MultiSelectTypeOptionContext typeOptionContext) - : service = TypeOptionService( - gridId: typeOptionContext.gridId, - fieldId: typeOptionContext.field.id, - ), - super(MultiSelectTypeOptionState.initial(typeOptionContext.typeOption)) { - on( - (event, emit) async { - await event.map( - createOption: (_CreateOption value) async { - final result = await service.newOption(name: value.optionName); - result.fold( - (option) { - emit(state.copyWith(typeOption: _insertOption(option))); - }, - (err) => Log.error(err), - ); - }, - updateOption: (_UpdateOption value) async { - emit(state.copyWith(typeOption: _updateOption(value.option))); - }, - deleteOption: (_DeleteOption value) { - emit(state.copyWith(typeOption: _deleteOption(value.option))); - }, - ); - }, - ); - } - - @override - Future close() async { - return super.close(); - } - - MultiSelectTypeOption _insertOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - typeOption.options.insert(0, option); - }); - } - - MultiSelectTypeOption _updateOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - final index = typeOption.options.indexWhere((element) => element.id == option.id); - if (index != -1) { - typeOption.options[index] = option; - } - }); - } - - MultiSelectTypeOption _deleteOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - final index = typeOption.options.indexWhere((element) => element.id == option.id); - if (index != -1) { - typeOption.options.removeAt(index); - } - }); - } -} - -@freezed -class MultiSelectTypeOptionEvent with _$MultiSelectTypeOptionEvent { - const factory MultiSelectTypeOptionEvent.createOption(String optionName) = _CreateOption; - const factory MultiSelectTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption; - const factory MultiSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption; -} - -@freezed -class MultiSelectTypeOptionState with _$MultiSelectTypeOptionState { - const factory MultiSelectTypeOptionState({ - required MultiSelectTypeOption typeOption, - }) = _MultiSelectTypeOptionState; - - factory MultiSelectTypeOptionState.initial(MultiSelectTypeOption typeOption) => MultiSelectTypeOptionState( - typeOption: typeOption, - ); -} diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart new file mode 100644 index 0000000000..bb72277f0b --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_type_option.dart @@ -0,0 +1,77 @@ +import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'dart:async'; +import 'package:protobuf/protobuf.dart'; +import 'select_option_type_option_bloc.dart'; +import 'type_option_service.dart'; + +class MultiSelectTypeOptionContext extends TypeOptionContext with SelectOptionTypeOptionAction { + final TypeOptionService service; + + MultiSelectTypeOptionContext({ + required MultiSelectTypeOptionDataBuilder dataBuilder, + required GridFieldContext fieldContext, + }) : service = TypeOptionService( + gridId: fieldContext.gridId, + fieldId: fieldContext.field.id, + ), + super(dataBuilder: dataBuilder, fieldContext: fieldContext); + + @override + List Function(SelectOption) get deleteOption { + return (SelectOption option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options.removeAt(index); + } + }); + return typeOption.options; + }; + } + + @override + Future> Function(String) get insertOption { + return (String optionName) { + return service.newOption(name: optionName).then((result) { + return result.fold( + (option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + typeOption.options.insert(0, option); + }); + + return typeOption.options; + }, + (err) { + Log.error(err); + return typeOption.options; + }, + ); + }); + }; + } + + @override + List Function(SelectOption) get udpateOption { + return (SelectOption option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options[index] = option; + } + }); + return typeOption.options; + }; + } +} + +class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + MultiSelectTypeOption fromBuffer(List buffer) { + return MultiSelectTypeOption.fromBuffer(buffer); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart index 7e1bf9a0d9..d4290cd8ff 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/select_option_type_option_bloc.dart @@ -5,26 +5,41 @@ import 'dart:async'; import 'package:dartz/dartz.dart'; part 'select_option_type_option_bloc.freezed.dart'; -class SelectOptionTypeOptionBloc extends Bloc { - SelectOptionTypeOptionBloc({required List options}) - : super(SelectOptionTyepOptionState.initial(options)) { +abstract class SelectOptionTypeOptionAction { + Future> Function(String) get insertOption; + + List Function(SelectOption) get deleteOption; + + List Function(SelectOption) get udpateOption; +} + +class SelectOptionTypeOptionBloc extends Bloc { + final SelectOptionTypeOptionAction typeOptionAction; + + SelectOptionTypeOptionBloc({ + required List options, + required this.typeOptionAction, + }) : super(SelectOptionTypeOptionState.initial(options)) { on( (event, emit) async { - await event.map( - createOption: (_CreateOption value) async { - emit(state.copyWith(isEditingOption: true, newOptionName: Some(value.optionName))); + await event.when( + createOption: (optionName) async { + final List options = await typeOptionAction.insertOption(optionName); + emit(state.copyWith(options: options)); }, - addingOption: (_AddingOption value) { + addingOption: () { emit(state.copyWith(isEditingOption: true, newOptionName: none())); }, - endAddingOption: (_EndAddingOption value) { + endAddingOption: () { emit(state.copyWith(isEditingOption: false, newOptionName: none())); }, - updateOption: (_UpdateOption value) { - emit(state.copyWith(updateOption: Some(value.option))); + updateOption: (option) { + final List options = typeOptionAction.udpateOption(option); + emit(state.copyWith(options: options)); }, - deleteOption: (_DeleteOption value) { - emit(state.copyWith(deleteOption: Some(value.option))); + deleteOption: (option) { + final List options = typeOptionAction.deleteOption(option); + emit(state.copyWith(options: options)); }, ); }, @@ -47,20 +62,16 @@ class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent { } @freezed -class SelectOptionTyepOptionState with _$SelectOptionTyepOptionState { - const factory SelectOptionTyepOptionState({ +class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState { + const factory SelectOptionTypeOptionState({ required List options, required bool isEditingOption, required Option newOptionName, - required Option updateOption, - required Option deleteOption, }) = _SelectOptionTyepOptionState; - factory SelectOptionTyepOptionState.initial(List options) => SelectOptionTyepOptionState( + factory SelectOptionTypeOptionState.initial(List options) => SelectOptionTypeOptionState( options: options, isEditingOption: false, newOptionName: none(), - updateOption: none(), - deleteOption: none(), ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart deleted file mode 100644 index 624e8fe08e..0000000000 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flowy_sdk/log.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'dart:async'; -import 'package:protobuf/protobuf.dart'; -import 'type_option_service.dart'; - -part 'single_select_bloc.freezed.dart'; - -typedef SingleSelectTypeOptionContext = TypeOptionContext; - -class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { - @override - SingleSelectTypeOption fromBuffer(List buffer) { - return SingleSelectTypeOption.fromBuffer(buffer); - } -} - -class SingleSelectTypeOptionBloc extends Bloc { - final TypeOptionService service; - - SingleSelectTypeOptionBloc( - SingleSelectTypeOptionContext typeOptionContext, - ) : service = TypeOptionService(gridId: typeOptionContext.gridId, fieldId: typeOptionContext.field.id), - super(SingleSelectTypeOptionState.initial(typeOptionContext.typeOption)) { - on( - (event, emit) async { - await event.map( - createOption: (_CreateOption value) async { - final result = await service.newOption(name: value.optionName); - result.fold( - (option) { - emit(state.copyWith(typeOption: _insertOption(option))); - }, - (err) => Log.error(err), - ); - }, - updateOption: (_UpdateOption value) async { - emit(state.copyWith(typeOption: _updateOption(value.option))); - }, - deleteOption: (_DeleteOption value) { - emit(state.copyWith(typeOption: _deleteOption(value.option))); - }, - ); - }, - ); - } - - @override - Future close() async { - return super.close(); - } - - SingleSelectTypeOption _insertOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - typeOption.options.insert(0, option); - }); - } - - SingleSelectTypeOption _updateOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - final index = typeOption.options.indexWhere((element) => element.id == option.id); - if (index != -1) { - typeOption.options[index] = option; - } - }); - } - - SingleSelectTypeOption _deleteOption(SelectOption option) { - state.typeOption.freeze(); - return state.typeOption.rebuild((typeOption) { - final index = typeOption.options.indexWhere((element) => element.id == option.id); - if (index != -1) { - typeOption.options.removeAt(index); - } - }); - } -} - -@freezed -class SingleSelectTypeOptionEvent with _$SingleSelectTypeOptionEvent { - const factory SingleSelectTypeOptionEvent.createOption(String optionName) = _CreateOption; - const factory SingleSelectTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption; - const factory SingleSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption; -} - -@freezed -class SingleSelectTypeOptionState with _$SingleSelectTypeOptionState { - const factory SingleSelectTypeOptionState({ - required SingleSelectTypeOption typeOption, - }) = _SingleSelectTypeOptionState; - - factory SingleSelectTypeOptionState.initial(SingleSelectTypeOption typeOption) => SingleSelectTypeOptionState( - typeOption: typeOption, - ); -} diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart new file mode 100644 index 0000000000..91d8de7a28 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_type_option.dart @@ -0,0 +1,78 @@ +import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'dart:async'; +import 'package:protobuf/protobuf.dart'; +import 'select_option_type_option_bloc.dart'; +import 'type_option_service.dart'; + +class SingleSelectTypeOptionContext extends TypeOptionContext + with SelectOptionTypeOptionAction { + final TypeOptionService service; + + SingleSelectTypeOptionContext({ + required SingleSelectTypeOptionDataBuilder dataBuilder, + required GridFieldContext fieldContext, + }) : service = TypeOptionService( + gridId: fieldContext.gridId, + fieldId: fieldContext.field.id, + ), + super(dataBuilder: dataBuilder, fieldContext: fieldContext); + + @override + List Function(SelectOption) get deleteOption { + return (SelectOption option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options.removeAt(index); + } + }); + return typeOption.options; + }; + } + + @override + Future> Function(String) get insertOption { + return (String optionName) { + return service.newOption(name: optionName).then((result) { + return result.fold( + (option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + typeOption.options.insert(0, option); + }); + + return typeOption.options; + }, + (err) { + Log.error(err); + return typeOption.options; + }, + ); + }); + }; + } + + @override + List Function(SelectOption) get udpateOption { + return (SelectOption option) { + typeOption.freeze(); + typeOption = typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options[index] = option; + } + }); + return typeOption.options; + }; + } +} + +class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + SingleSelectTypeOption fromBuffer(List buffer) { + return SingleSelectTypeOption.fromBuffer(buffer); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart index c088618825..4bf61cc1ff 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart @@ -64,7 +64,7 @@ class TypeOptionContext { set typeOption(T typeOption) { _fieldContext.typeOptionData = typeOption.writeToBuffer(); - _typeOptionObject = null; + _typeOptionObject = typeOption; } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart index 3a0ebc5112..6ea198f303 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart @@ -13,7 +13,7 @@ export 'field/field_editor_pannel_bloc.dart'; // Field Type Option export 'field/type_option/date_bloc.dart'; export 'field/type_option/number_bloc.dart'; -export 'field/type_option/single_select_bloc.dart'; +export 'field/type_option/single_select_type_option.dart'; // Cell export 'cell/text_cell_bloc.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart index b5860b1fbb..43d394bfe1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart @@ -7,6 +7,7 @@ class InputTextField extends StatefulWidget { final void Function(String)? onDone; final void Function(String)? onChanged; final void Function() onCanceled; + final bool autoClearWhenDone; final String text; const InputTextField({ @@ -14,6 +15,7 @@ class InputTextField extends StatefulWidget { this.onDone, required this.onCanceled, this.onChanged, + this.autoClearWhenDone = false, Key? key, }) : super(key: key); @@ -57,6 +59,10 @@ class _InputTextFieldState extends State { if (widget.onDone != null) { widget.onDone!(_controller.text); } + + if (widget.autoClearWhenDone) { + _controller.text = ""; + } }, ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart index 30599cef30..eb42267445 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart @@ -1,6 +1,6 @@ import 'dart:typed_data'; -import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart'; import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart'; import 'package:dartz/dartz.dart' show Either; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart index b1b530fcf3..fc310410f9 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart @@ -1,7 +1,6 @@ -import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'select_option.dart'; @@ -32,32 +31,12 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => MultiSelectTypeOptionBloc(typeOptionContext), - child: BlocConsumer( - listener: (context, state) { - typeOptionContext.typeOption = state.typeOption; - }, - builder: (context, state) { - return SelectOptionTypeOptionWidget( - options: state.typeOption.options, - beginEdit: () { - overlayDelegate.hideOverlay(context); - }, - createSelectOptionCallback: (name) { - context.read().add(MultiSelectTypeOptionEvent.createOption(name)); - }, - updateSelectOptionCallback: (option) { - context.read().add(MultiSelectTypeOptionEvent.updateOption(option)); - }, - deleteSelectOptionCallback: (option) { - context.read().add(MultiSelectTypeOptionEvent.deleteOption(option)); - }, - overlayDelegate: overlayDelegate, - // key: ValueKey(state.typeOption.hashCode), - ); - }, - ), + return SelectOptionTypeOptionWidget( + options: typeOptionContext.typeOption.options, + beginEdit: () => overlayDelegate.hideOverlay(context), + overlayDelegate: overlayDelegate, + typeOptionAction: typeOptionContext, + // key: ValueKey(state.typeOption.hashCode), ); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart index 68711a0ad4..b308995dd1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option.dart @@ -19,45 +19,22 @@ import 'select_option_editor.dart'; class SelectOptionTypeOptionWidget extends StatelessWidget { final List options; final VoidCallback beginEdit; - final Function(String optionName) createSelectOptionCallback; - final Function(SelectOption) updateSelectOptionCallback; - final Function(SelectOption) deleteSelectOptionCallback; final TypeOptionOverlayDelegate overlayDelegate; + final SelectOptionTypeOptionAction typeOptionAction; const SelectOptionTypeOptionWidget({ required this.options, required this.beginEdit, - required this.createSelectOptionCallback, - required this.updateSelectOptionCallback, - required this.deleteSelectOptionCallback, required this.overlayDelegate, + required this.typeOptionAction, Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SelectOptionTypeOptionBloc(options: options), - child: BlocConsumer( - listener: (context, state) { - if (state.isEditingOption) { - beginEdit(); - } - state.newOptionName.fold( - () => null, - (optionName) => createSelectOptionCallback(optionName), - ); - - state.updateOption.fold( - () => null, - (updateOption) => updateSelectOptionCallback(updateOption), - ); - - state.deleteOption.fold( - () => null, - (deleteOption) => deleteSelectOptionCallback(deleteOption), - ); - }, + create: (context) => SelectOptionTypeOptionBloc(options: options, typeOptionAction: typeOptionAction), + child: BlocBuilder( builder: (context, state) { List children = [ const TypeOptionSeparator(), @@ -83,7 +60,7 @@ class OptionTitle extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { List children = [FlowyText.medium(LocaleKeys.grid_field_optionTitle.tr(), fontSize: 12)]; if (state.options.isNotEmpty) { @@ -130,7 +107,7 @@ class _OptionList extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( buildWhen: (previous, current) { return previous.options != current.options; }, @@ -230,13 +207,19 @@ class _CreateOptionTextField extends StatelessWidget { @override Widget build(BuildContext context) { - return InputTextField( - text: "", - onCanceled: () { - context.read().add(const SelectOptionTypeOptionEvent.endAddingOption()); - }, - onDone: (optionName) { - context.read().add(SelectOptionTypeOptionEvent.createOption(optionName)); + return BlocBuilder( + builder: (context, state) { + final text = state.newOptionName.foldRight("", (a, previous) => a); + return InputTextField( + autoClearWhenDone: true, + text: text, + onCanceled: () { + context.read().add(const SelectOptionTypeOptionEvent.endAddingOption()); + }, + onDone: (optionName) { + context.read().add(SelectOptionTypeOptionEvent.createOption(optionName)); + }, + ); }, ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart index bbe3b1550b..fedddec11d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart @@ -1,7 +1,6 @@ -import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_type_option.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'select_option.dart'; class SingleSelectTypeOptionBuilder extends TypeOptionBuilder { @@ -31,32 +30,12 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => SingleSelectTypeOptionBloc(typeOptionContext), - child: BlocConsumer( - listener: (context, state) { - typeOptionContext.typeOption = state.typeOption; - }, - builder: (context, state) { - return SelectOptionTypeOptionWidget( - options: state.typeOption.options, - beginEdit: () { - overlayDelegate.hideOverlay(context); - }, - createSelectOptionCallback: (name) { - context.read().add(SingleSelectTypeOptionEvent.createOption(name)); - }, - updateSelectOptionCallback: (option) { - context.read().add(SingleSelectTypeOptionEvent.updateOption(option)); - }, - deleteSelectOptionCallback: (option) { - context.read().add(SingleSelectTypeOptionEvent.deleteOption(option)); - }, - overlayDelegate: overlayDelegate, - // key: ValueKey(state.typeOption.hashCode), - ); - }, - ), + return SelectOptionTypeOptionWidget( + options: typeOptionContext.typeOption.options, + beginEdit: () => overlayDelegate.hideOverlay(context), + overlayDelegate: overlayDelegate, + typeOptionAction: typeOptionContext, + // key: ValueKey(state.typeOption.hashCode), ); } } From 23900b49f50d3293b1624fb30e15996597a8ffb5 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 19 May 2022 10:41:00 +0800 Subject: [PATCH 07/82] chore: fix bugs --- .../grid/field/field_editor_bloc.dart | 6 +- .../application/grid/field/field_service.dart | 66 ++--- .../grid/src/widgets/header/field_cell.dart | 2 +- .../grid/src/widgets/header/field_editor.dart | 3 +- .../src/widgets/header/field_name_input.dart | 30 +- .../grid/src/widgets/row/row_detail.dart | 2 +- .../src/widgets/toolbar/grid_property.dart | 2 +- .../dart_event/flowy-grid/dart_event.dart | 17 ++ .../flowy-grid-data-model/grid.pb.dart | 27 +- .../flowy-grid-data-model/grid.pbjson.dart | 8 +- .../protobuf/flowy-grid/event_map.pbenum.dart | 2 + .../protobuf/flowy-grid/event_map.pbjson.dart | 3 +- .../rust-lib/flowy-grid/src/event_handler.rs | 82 +++--- frontend/rust-lib/flowy-grid/src/event_map.rs | 4 + .../src/protobuf/model/event_map.rs | 19 +- .../src/protobuf/proto/event_map.proto | 1 + .../flowy-grid/src/services/grid_editor.rs | 12 +- .../src/entities/grid.rs | 31 ++- .../src/protobuf/model/grid.rs | 262 +++++++++--------- .../src/protobuf/proto/grid.proto | 3 +- 20 files changed, 322 insertions(+), 260 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart index ba2c25fec6..2815d5519d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart @@ -9,7 +9,7 @@ class FieldEditorBloc extends Bloc { FieldEditorBloc({ required String gridId, required String fieldName, - required FieldContextLoader fieldContextLoader, + required IFieldContextLoader fieldContextLoader, }) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) { on( (event, emit) async { @@ -18,7 +18,7 @@ class FieldEditorBloc extends Bloc { final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader); await fieldContext.loadData().then((result) { result.fold( - (l) => emit(state.copyWith(fieldContext: Some(fieldContext))), + (l) => emit(state.copyWith(fieldContext: Some(fieldContext), name: fieldContext.field.name)), (r) => null, ); }); @@ -53,7 +53,7 @@ class FieldEditorState with _$FieldEditorState { required Option fieldContext, }) = _FieldEditorState; - factory FieldEditorState.initial(String gridId, String fieldName, FieldContextLoader loader) => FieldEditorState( + factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState( gridId: gridId, fieldContext: none(), errorText: '', diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart index 03f27dffe7..36547071b9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart @@ -15,15 +15,6 @@ class FieldService { FieldService({required this.gridId, required this.fieldId}); - Future> switchToField(FieldType fieldType) { - final payload = EditFieldPayload.create() - ..gridId = gridId - ..fieldId = fieldId - ..fieldType = fieldType; - - return GridEventSwitchToField(payload).send(); - } - Future> moveField(int fromIndex, int toIndex) { final payload = MoveItemPayload.create() ..gridId = gridId @@ -146,13 +137,22 @@ class GridFieldCellContext with _$GridFieldCellContext { }) = _GridFieldCellContext; } -abstract class FieldContextLoader { +abstract class IFieldContextLoader { + String get gridId; Future> load(); - Future> switchToField(String fieldId, FieldType fieldType); + Future> switchToField(String fieldId, FieldType fieldType) { + final payload = EditFieldPayload.create() + ..gridId = gridId + ..fieldId = fieldId + ..fieldType = fieldType; + + return GridEventSwitchToField(payload).send(); + } } -class NewFieldContextLoader extends FieldContextLoader { +class NewFieldContextLoader extends IFieldContextLoader { + @override final String gridId; NewFieldContextLoader({ required this.gridId, @@ -164,24 +164,16 @@ class NewFieldContextLoader extends FieldContextLoader { ..gridId = gridId ..fieldType = FieldType.RichText; - return GridEventGetFieldTypeOption(payload).send(); - } - - @override - Future> switchToField(String fieldId, FieldType fieldType) { - final payload = EditFieldPayload.create() - ..gridId = gridId - ..fieldType = fieldType; - - return GridEventGetFieldTypeOption(payload).send(); + return GridEventCreateFieldTypeOption(payload).send(); } } -class DefaultFieldContextLoader extends FieldContextLoader { +class FieldContextLoader extends IFieldContextLoader { + @override final String gridId; final Field field; - DefaultFieldContextLoader({ + FieldContextLoader({ required this.gridId, required this.field, }); @@ -195,24 +187,18 @@ class DefaultFieldContextLoader extends FieldContextLoader { return GridEventGetFieldTypeOption(payload).send(); } - - @override - Future> switchToField(String fieldId, FieldType fieldType) async { - final fieldService = FieldService(gridId: gridId, fieldId: fieldId); - return fieldService.switchToField(fieldType); - } } class GridFieldContext { final String gridId; - final FieldContextLoader _loader; + final IFieldContextLoader _loader; late FieldTypeOptionData _data; ValueNotifier? _fieldNotifier; GridFieldContext({ required this.gridId, - required FieldContextLoader loader, + required IFieldContextLoader loader, }) : _loader = loader; Future> loadData() async { @@ -246,18 +232,18 @@ class GridFieldContext { List get typeOptionData => _data.typeOptionData; set fieldName(String name) { - _updateData(name: name); + _updateData(newName: name); } set typeOptionData(List typeOptionData) { - _updateData(typeOptionData: typeOptionData); + _updateData(newTypeOptionData: typeOptionData); } - void _updateData({String? name, Field? newField, List? typeOptionData}) { + void _updateData({String? newName, Field? newField, List? newTypeOptionData}) { _data = _data.rebuild((rebuildData) { - if (name != null) { + if (newName != null) { rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) { - rebuildField.name = name; + rebuildField.name = newName; }); } @@ -265,8 +251,8 @@ class GridFieldContext { rebuildData.field_2 = newField; } - if (typeOptionData != null) { - rebuildData.typeOptionData = typeOptionData; + if (newTypeOptionData != null) { + rebuildData.typeOptionData = newTypeOptionData; } }); @@ -287,7 +273,7 @@ class GridFieldContext { (fieldTypeOptionData) { _updateData( newField: fieldTypeOptionData.field_2, - typeOptionData: fieldTypeOptionData.typeOptionData, + newTypeOptionData: fieldTypeOptionData.typeOptionData, ); }, (err) { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 9eec395ce0..48866457f7 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -64,7 +64,7 @@ class GridFieldCell extends StatelessWidget { FieldEditor( gridId: state.gridId, fieldName: state.field.name, - contextLoader: DefaultFieldContextLoader( + contextLoader: FieldContextLoader( gridId: state.gridId, field: state.field, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart index b3d96949f0..3390351663 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart @@ -14,7 +14,7 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate { final String gridId; final String fieldName; - final FieldContextLoader contextLoader; + final IFieldContextLoader contextLoader; const FieldEditor({ required this.gridId, required this.fieldName, @@ -97,7 +97,6 @@ class _FieldNameTextField extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder( - buildWhen: (p, c) => p.name != c.name, builder: (context, state) { return FieldNameTextField( name: state.name, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_name_input.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_name_input.dart index 80cb327379..acc1256cbc 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_name_input.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_name_input.dart @@ -3,7 +3,7 @@ import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class FieldNameTextField extends StatelessWidget { +class FieldNameTextField extends StatefulWidget { final void Function(String) onNameChanged; final String name; final String errorText; @@ -14,19 +14,41 @@ class FieldNameTextField extends StatelessWidget { Key? key, }) : super(key: key); + @override + State createState() => _FieldNameTextFieldState(); +} + +class _FieldNameTextFieldState extends State { + late String name; + TextEditingController controller = TextEditingController(); + + @override + void initState() { + controller.text = widget.name; + super.initState(); + } + @override Widget build(BuildContext context) { final theme = context.watch(); return RoundedInputField( height: 36, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), - initialValue: name, + controller: controller, normalBorderColor: theme.shader4, errorBorderColor: theme.red, focusBorderColor: theme.main1, cursorColor: theme.main1, - errorText: errorText, - onChanged: onNameChanged, + errorText: widget.errorText, + onChanged: widget.onNameChanged, ); } + + @override + void didUpdateWidget(covariant FieldNameTextField oldWidget) { + controller.text = widget.name; + controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length)); + + super.didUpdateWidget(oldWidget); + } } 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 c8672d4ae3..41cbcb1cc1 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 @@ -179,7 +179,7 @@ class _RowDetailCell extends StatelessWidget { FieldEditor( gridId: gridCell.gridId, fieldName: gridCell.field.name, - contextLoader: DefaultFieldContextLoader( + contextLoader: FieldContextLoader( gridId: gridCell.gridId, field: gridCell.field, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart index ec5ca40b1e..c971b48479 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart @@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget { FieldEditor( gridId: gridId, fieldName: field.name, - contextLoader: DefaultFieldContextLoader(gridId: gridId, field: field), + contextLoader: FieldContextLoader(gridId: gridId, field: field), ).show(context, anchorDirection: AnchorDirection.bottomRight); }, ); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart index 95c17f46cf..bf4964054e 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart @@ -188,6 +188,23 @@ class GridEventGetFieldTypeOption { } } +class GridEventCreateFieldTypeOption { + EditFieldPayload request; + GridEventCreateFieldTypeOption(this.request); + + Future> send() { + final request = FFIRequest.create() + ..event = GridEvent.CreateFieldTypeOption.toString() + ..payload = requestToBytes(this.request); + + return Dispatch.asyncRequest(request) + .then((bytesResult) => bytesResult.fold( + (okBytes) => left(FieldTypeOptionData.fromBuffer(okBytes)), + (errBytes) => right(FlowyError.fromBuffer(errBytes)), + )); + } +} + class GridEventNewSelectOption { CreateSelectOptionPayload request; GridEventNewSelectOption(this.request); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index ae450b2e6d..a1ecd8c059 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -490,21 +490,12 @@ class GetEditFieldContextPayload extends $pb.GeneratedMessage { void clearFieldType() => clearField(3); } -enum EditFieldPayload_OneOfFieldId { - fieldId, - notSet -} - class EditFieldPayload extends $pb.GeneratedMessage { - static const $core.Map<$core.int, EditFieldPayload_OneOfFieldId> _EditFieldPayload_OneOfFieldIdByTag = { - 2 : EditFieldPayload_OneOfFieldId.fieldId, - 0 : EditFieldPayload_OneOfFieldId.notSet - }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldPayload', createEmptyInstance: create) - ..oo(0, [2]) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') ..e(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values) + ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createIfNotExist') ..hasRequiredFields = false ; @@ -513,6 +504,7 @@ class EditFieldPayload extends $pb.GeneratedMessage { $core.String? gridId, $core.String? fieldId, FieldType? fieldType, + $core.bool? createIfNotExist, }) { final _result = create(); if (gridId != null) { @@ -524,6 +516,9 @@ class EditFieldPayload extends $pb.GeneratedMessage { if (fieldType != null) { _result.fieldType = fieldType; } + if (createIfNotExist != null) { + _result.createIfNotExist = createIfNotExist; + } return _result; } factory EditFieldPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); @@ -547,9 +542,6 @@ class EditFieldPayload extends $pb.GeneratedMessage { static EditFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static EditFieldPayload? _defaultInstance; - EditFieldPayload_OneOfFieldId whichOneOfFieldId() => _EditFieldPayload_OneOfFieldIdByTag[$_whichOneof(0)]!; - void clearOneOfFieldId() => clearField($_whichOneof(0)); - @$pb.TagNumber(1) $core.String get gridId => $_getSZ(0); @$pb.TagNumber(1) @@ -576,6 +568,15 @@ class EditFieldPayload extends $pb.GeneratedMessage { $core.bool hasFieldType() => $_has(2); @$pb.TagNumber(3) void clearFieldType() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get createIfNotExist => $_getBF(3); + @$pb.TagNumber(4) + set createIfNotExist($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasCreateIfNotExist() => $_has(3); + @$pb.TagNumber(4) + void clearCreateIfNotExist() => clearField(4); } class FieldTypeOptionContext extends $pb.GeneratedMessage { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index 78e834edb7..3a925cb282 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -117,16 +117,14 @@ const EditFieldPayload$json = const { '1': 'EditFieldPayload', '2': const [ const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'}, - const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'}, + const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '10': 'fieldId'}, const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'}, - ], - '8': const [ - const {'1': 'one_of_field_id'}, + const {'1': 'create_if_not_exist', '3': 4, '4': 1, '5': 8, '10': 'createIfNotExist'}, ], }; /// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIbCghmaWVsZF9pZBgCIAEoCUgAUgdmaWVsZElkEikKCmZpZWxkX3R5cGUYAyABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZUIRCg9vbmVfb2ZfZmllbGRfaWQ='); +final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGUSLQoTY3JlYXRlX2lmX25vdF9leGlzdBgEIAEoCFIQY3JlYXRlSWZOb3RFeGlzdA=='); @$core.Deprecated('Use fieldTypeOptionContextDescriptor instead') const FieldTypeOptionContext$json = const { '1': 'FieldTypeOptionContext', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart index f9945ab604..341dcf8d6a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart @@ -21,6 +21,7 @@ class GridEvent extends $pb.ProtobufEnum { static const GridEvent DuplicateField = GridEvent._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField'); static const GridEvent MoveItem = GridEvent._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveItem'); static const GridEvent GetFieldTypeOption = GridEvent._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetFieldTypeOption'); + static const GridEvent CreateFieldTypeOption = GridEvent._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateFieldTypeOption'); static const GridEvent NewSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewSelectOption'); static const GridEvent GetSelectOptionCellData = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptionCellData'); static const GridEvent UpdateSelectOption = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOption'); @@ -46,6 +47,7 @@ class GridEvent extends $pb.ProtobufEnum { DuplicateField, MoveItem, GetFieldTypeOption, + CreateFieldTypeOption, NewSelectOption, GetSelectOptionCellData, UpdateSelectOption, diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart index 89842a54d0..58a712a4da 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart @@ -23,6 +23,7 @@ const GridEvent$json = const { const {'1': 'DuplicateField', '2': 21}, const {'1': 'MoveItem', '2': 22}, const {'1': 'GetFieldTypeOption', '2': 23}, + const {'1': 'CreateFieldTypeOption', '2': 24}, const {'1': 'NewSelectOption', '2': 30}, const {'1': 'GetSelectOptionCellData', '2': 31}, const {'1': 'UpdateSelectOption', '2': 32}, @@ -39,4 +40,4 @@ const GridEvent$json = const { }; /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo='); +final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo='); diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index a8c8b99e68..724d898d31 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -2,7 +2,6 @@ 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 crate::services::grid_editor::ClientGridEditor; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::*; use lib_dispatch::prelude::{data_result, AppData, Data, DataResult}; @@ -100,15 +99,24 @@ pub(crate) async fn switch_to_field_handler( manager: AppData>, ) -> DataResult { let params: EditFieldParams = data.into_inner().try_into()?; - if params.field_id.is_none() { - return Err(ErrorCode::FieldIdIsEmpty.into()); - } - let field_id = params.field_id.unwrap(); let editor = manager.get_grid_editor(¶ms.grid_id)?; - editor.switch_to_field_type(&field_id, ¶ms.field_type).await?; - let field_meta = editor.get_field_meta(&field_id).await; - let data = - make_field_type_option_data(¶ms.grid_id, Some(field_id), params.field_type, editor, field_meta).await?; + editor + .switch_to_field_type(¶ms.field_id, ¶ms.field_type) + .await?; + + // Get the FieldMeta with field_id, if it doesn't exist, we create the default FieldMeta from the FieldType. + let field_meta = editor + .get_field_meta(¶ms.field_id) + .await + .unwrap_or(editor.next_field_meta(¶ms.field_type).await?); + + let type_option_data = get_type_option_data(&field_meta, ¶ms.field_type).await?; + let data = FieldTypeOptionData { + grid_id: params.grid_id, + field: field_meta.into(), + type_option_data, + }; + data_result(data) } @@ -123,6 +131,7 @@ pub(crate) async fn duplicate_field_handler( Ok(()) } +/// Return the FieldTypeOptionData if the Field exists otherwise return record not found error. #[tracing::instrument(level = "debug", skip(data, manager), err)] pub(crate) async fn get_field_type_option_data_handler( data: Data, @@ -130,7 +139,29 @@ pub(crate) async fn get_field_type_option_data_handler( ) -> DataResult { let params: EditFieldParams = data.into_inner().try_into()?; let editor = manager.get_grid_editor(¶ms.grid_id)?; - let field_meta = get_or_create_field_meta(params.field_id, ¶ms.field_type, editor).await?; + match editor.get_field_meta(¶ms.field_id).await { + None => Err(FlowyError::record_not_found()), + Some(field_meta) => { + let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?; + let data = FieldTypeOptionData { + grid_id: params.grid_id, + field: field_meta.into(), + type_option_data, + }; + data_result(data) + } + } +} + +/// Create FieldMeta and save it. Return the FieldTypeOptionData. +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn create_field_type_option_data_handler( + data: Data, + manager: AppData>, +) -> DataResult { + let params: CreateFieldParams = data.into_inner().try_into()?; + let editor = manager.get_grid_editor(¶ms.grid_id)?; + let field_meta = editor.create_next_field_meta(¶ms.field_type).await?; let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?; data_result(FieldTypeOptionData { @@ -151,23 +182,6 @@ pub(crate) async fn move_item_handler( Ok(()) } -async fn make_field_type_option_data( - grid_id: &str, - field_id: Option, - field_type: FieldType, - editor: Arc, - field_meta: Option, -) -> FlowyResult { - let field_meta = field_meta.unwrap_or(get_or_create_field_meta(field_id, &field_type, editor).await?); - let type_option_data = get_type_option_data(&field_meta, &field_type).await?; - - Ok(FieldTypeOptionData { - grid_id: grid_id.to_string(), - field: field_meta.into(), - type_option_data, - }) -} - /// The FieldMeta contains multiple data, each of them belongs to a specific FieldType. async fn get_type_option_data(field_meta: &FieldMeta, field_type: &FieldType) -> FlowyResult> { let s = field_meta @@ -179,20 +193,6 @@ async fn get_type_option_data(field_meta: &FieldMeta, field_type: &FieldType) -> Ok(type_option_data) } -async fn get_or_create_field_meta( - field_id: Option, - field_type: &FieldType, - editor: Arc, -) -> FlowyResult { - match field_id { - None => editor.create_next_field_meta(field_type).await, - Some(field_id) => match editor.get_field_meta(&field_id).await { - None => editor.create_next_field_meta(field_type).await, - Some(field_meta) => Ok(field_meta), - }, - } -} - #[tracing::instrument(level = "debug", skip(data, manager), err)] pub(crate) async fn get_row_handler( data: Data, diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index fe749262ef..c740eee6d8 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -20,6 +20,7 @@ pub fn create(grid_manager: Arc) -> Module { .event(GridEvent::DuplicateField, duplicate_field_handler) .event(GridEvent::MoveItem, move_item_handler) .event(GridEvent::GetFieldTypeOption, get_field_type_option_data_handler) + .event(GridEvent::CreateFieldTypeOption, create_field_type_option_data_handler) // Row .event(GridEvent::CreateRow, create_row_handler) .event(GridEvent::GetRow, get_row_handler) @@ -76,6 +77,9 @@ pub enum GridEvent { #[event(input = "EditFieldPayload", output = "FieldTypeOptionData")] GetFieldTypeOption = 23, + #[event(input = "EditFieldPayload", output = "FieldTypeOptionData")] + CreateFieldTypeOption = 24, + #[event(input = "CreateSelectOptionPayload", output = "SelectOption")] NewSelectOption = 30, diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs index f6c4861c29..6762254df7 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs @@ -36,6 +36,7 @@ pub enum GridEvent { DuplicateField = 21, MoveItem = 22, GetFieldTypeOption = 23, + CreateFieldTypeOption = 24, NewSelectOption = 30, GetSelectOptionCellData = 31, UpdateSelectOption = 32, @@ -68,6 +69,7 @@ impl ::protobuf::ProtobufEnum for GridEvent { 21 => ::std::option::Option::Some(GridEvent::DuplicateField), 22 => ::std::option::Option::Some(GridEvent::MoveItem), 23 => ::std::option::Option::Some(GridEvent::GetFieldTypeOption), + 24 => ::std::option::Option::Some(GridEvent::CreateFieldTypeOption), 30 => ::std::option::Option::Some(GridEvent::NewSelectOption), 31 => ::std::option::Option::Some(GridEvent::GetSelectOptionCellData), 32 => ::std::option::Option::Some(GridEvent::UpdateSelectOption), @@ -97,6 +99,7 @@ impl ::protobuf::ProtobufEnum for GridEvent { GridEvent::DuplicateField, GridEvent::MoveItem, GridEvent::GetFieldTypeOption, + GridEvent::CreateFieldTypeOption, GridEvent::NewSelectOption, GridEvent::GetSelectOptionCellData, GridEvent::UpdateSelectOption, @@ -137,18 +140,18 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fevent_map.proto*\xc1\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ + \n\x0fevent_map.proto*\xdc\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\ \x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\ \x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\ SwitchToField\x10\x14\x12\x12\n\x0eDuplicateField\x10\x15\x12\x0c\n\x08M\ - oveItem\x10\x16\x12\x16\n\x12GetFieldTypeOption\x10\x17\x12\x13\n\x0fNew\ - SelectOption\x10\x1e\x12\x1b\n\x17GetSelectOptionCellData\x10\x1f\x12\ - \x16\n\x12UpdateSelectOption\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06\ - GetRow\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\ - \x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSel\ - ectOptionCell\x10H\x12\x12\n\x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDate\ - CellData\x10Zb\x06proto3\ + oveItem\x10\x16\x12\x16\n\x12GetFieldTypeOption\x10\x17\x12\x19\n\x15Cre\ + ateFieldTypeOption\x10\x18\x12\x13\n\x0fNewSelectOption\x10\x1e\x12\x1b\ + \n\x17GetSelectOptionCellData\x10\x1f\x12\x16\n\x12UpdateSelectOption\ + \x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteR\ + ow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\ + \n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\ + \x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto index a0c88a7187..10e98a2934 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto @@ -12,6 +12,7 @@ enum GridEvent { DuplicateField = 21; MoveItem = 22; GetFieldTypeOption = 23; + CreateFieldTypeOption = 24; NewSelectOption = 30; GetSelectOptionCellData = 31; UpdateSelectOption = 32; 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 b0695af1fd..44c826f31d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -121,12 +121,22 @@ impl ClientGridEditor { Ok(()) } - pub async fn create_next_field_meta(&self, field_type: &FieldType) -> FlowyResult { + pub async fn next_field_meta(&self, field_type: &FieldType) -> FlowyResult { let name = format!("Property {}", self.grid_pad.read().await.fields().len() + 1); let field_meta = FieldBuilder::from_field_type(field_type).name(&name).build(); Ok(field_meta) } + pub async fn create_next_field_meta(&self, field_type: &FieldType) -> FlowyResult { + let field_meta = self.next_field_meta(field_type).await?; + let _ = self + .modify(|grid| Ok(grid.create_field_meta(field_meta.clone(), None)?)) + .await?; + let _ = self.notify_did_insert_grid_field(&field_meta.id).await?; + + Ok(field_meta) + } + pub async fn contain_field(&self, field_id: &str) -> bool { self.grid_pad.read().await.contain_field(field_id) } 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 06b4f7222f..5cde193623 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -167,16 +167,19 @@ pub struct EditFieldPayload { #[pb(index = 1)] pub grid_id: String, - #[pb(index = 2, one_of)] - pub field_id: Option, + #[pb(index = 2)] + pub field_id: String, #[pb(index = 3)] pub field_type: FieldType, + + #[pb(index = 4)] + pub create_if_not_exist: bool, } pub struct EditFieldParams { pub grid_id: String, - pub field_id: Option, + pub field_id: String, pub field_type: FieldType, } @@ -185,10 +188,28 @@ impl TryInto for EditFieldPayload { fn try_into(self) -> Result { let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; - // let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?; + let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?; Ok(EditFieldParams { grid_id: grid_id.0, - field_id: self.field_id, + field_id: field_id.0, + field_type: self.field_type, + }) + } +} + +pub struct CreateFieldParams { + pub grid_id: String, + pub field_type: FieldType, +} + +impl TryInto for EditFieldPayload { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; + + Ok(CreateFieldParams { + grid_id: grid_id.0, field_type: self.field_type, }) } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 6df3c847f0..0cda103da8 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -1644,9 +1644,9 @@ impl ::protobuf::reflect::ProtobufValue for GetEditFieldContextPayload { pub struct EditFieldPayload { // message fields pub grid_id: ::std::string::String, + pub field_id: ::std::string::String, pub field_type: FieldType, - // message oneof groups - pub one_of_field_id: ::std::option::Option, + pub create_if_not_exist: bool, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -1658,11 +1658,6 @@ impl<'a> ::std::default::Default for &'a EditFieldPayload { } } -#[derive(Clone,PartialEq,Debug)] -pub enum EditFieldPayload_oneof_one_of_field_id { - field_id(::std::string::String), -} - impl EditFieldPayload { pub fn new() -> EditFieldPayload { ::std::default::Default::default() @@ -1698,49 +1693,26 @@ impl EditFieldPayload { pub fn get_field_id(&self) -> &str { - match self.one_of_field_id { - ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(ref v)) => v, - _ => "", - } + &self.field_id } pub fn clear_field_id(&mut self) { - self.one_of_field_id = ::std::option::Option::None; - } - - pub fn has_field_id(&self) -> bool { - match self.one_of_field_id { - ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(..)) => true, - _ => false, - } + self.field_id.clear(); } // Param is passed by value, moved pub fn set_field_id(&mut self, v: ::std::string::String) { - self.one_of_field_id = ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(v)) + self.field_id = v; } // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. pub fn mut_field_id(&mut self) -> &mut ::std::string::String { - if let ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(_)) = self.one_of_field_id { - } else { - self.one_of_field_id = ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(::std::string::String::new())); - } - match self.one_of_field_id { - ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(ref mut v)) => v, - _ => panic!(), - } + &mut self.field_id } // Take field pub fn take_field_id(&mut self) -> ::std::string::String { - if self.has_field_id() { - match self.one_of_field_id.take() { - ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(v)) => v, - _ => panic!(), - } - } else { - ::std::string::String::new() - } + ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) } // .FieldType field_type = 3; @@ -1757,6 +1729,21 @@ impl EditFieldPayload { pub fn set_field_type(&mut self, v: FieldType) { self.field_type = v; } + + // bool create_if_not_exist = 4; + + + pub fn get_create_if_not_exist(&self) -> bool { + self.create_if_not_exist + } + pub fn clear_create_if_not_exist(&mut self) { + self.create_if_not_exist = false; + } + + // Param is passed by value, moved + pub fn set_create_if_not_exist(&mut self, v: bool) { + self.create_if_not_exist = v; + } } impl ::protobuf::Message for EditFieldPayload { @@ -1772,14 +1759,18 @@ impl ::protobuf::Message for EditFieldPayload { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?; }, 2 => { - if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { - return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); - } - self.one_of_field_id = ::std::option::Option::Some(EditFieldPayload_oneof_one_of_field_id::field_id(is.read_string()?)); + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; }, 3 => { ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 3, &mut self.unknown_fields)? }, + 4 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_bool()?; + self.create_if_not_exist = tmp; + }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, @@ -1795,15 +1786,14 @@ impl ::protobuf::Message for EditFieldPayload { if !self.grid_id.is_empty() { my_size += ::protobuf::rt::string_size(1, &self.grid_id); } + if !self.field_id.is_empty() { + my_size += ::protobuf::rt::string_size(2, &self.field_id); + } if self.field_type != FieldType::RichText { my_size += ::protobuf::rt::enum_size(3, self.field_type); } - if let ::std::option::Option::Some(ref v) = self.one_of_field_id { - match v { - &EditFieldPayload_oneof_one_of_field_id::field_id(ref v) => { - my_size += ::protobuf::rt::string_size(2, &v); - }, - }; + if self.create_if_not_exist != false { + my_size += 2; } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -1814,15 +1804,14 @@ impl ::protobuf::Message for EditFieldPayload { if !self.grid_id.is_empty() { os.write_string(1, &self.grid_id)?; } + if !self.field_id.is_empty() { + os.write_string(2, &self.field_id)?; + } if self.field_type != FieldType::RichText { os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.field_type))?; } - if let ::std::option::Option::Some(ref v) = self.one_of_field_id { - match v { - &EditFieldPayload_oneof_one_of_field_id::field_id(ref v) => { - os.write_string(2, v)?; - }, - }; + if self.create_if_not_exist != false { + os.write_bool(4, self.create_if_not_exist)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -1867,16 +1856,21 @@ impl ::protobuf::Message for EditFieldPayload { |m: &EditFieldPayload| { &m.grid_id }, |m: &mut EditFieldPayload| { &mut m.grid_id }, )); - fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>( + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( "field_id", - EditFieldPayload::has_field_id, - EditFieldPayload::get_field_id, + |m: &EditFieldPayload| { &m.field_id }, + |m: &mut EditFieldPayload| { &mut m.field_id }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( "field_type", |m: &EditFieldPayload| { &m.field_type }, |m: &mut EditFieldPayload| { &mut m.field_type }, )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>( + "create_if_not_exist", + |m: &EditFieldPayload| { &m.create_if_not_exist }, + |m: &mut EditFieldPayload| { &mut m.create_if_not_exist }, + )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "EditFieldPayload", fields, @@ -1894,8 +1888,9 @@ impl ::protobuf::Message for EditFieldPayload { impl ::protobuf::Clear for EditFieldPayload { fn clear(&mut self) { self.grid_id.clear(); - self.one_of_field_id = ::std::option::Option::None; + self.field_id.clear(); self.field_type = FieldType::RichText; + self.create_if_not_exist = false; self.unknown_fields.clear(); } } @@ -8311,83 +8306,84 @@ static file_descriptor_proto_data: &'static [u8] = b"\ extPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1b\n\ \x08field_id\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\n\nfield_type\x18\ \x03\x20\x01(\x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fone_of_field_id\"\ - \x86\x01\n\x10EditFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ - \x06gridId\x12\x1b\n\x08field_id\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\ - \n\nfield_type\x18\x03\x20\x01(\x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fo\ - ne_of_field_id\"\x82\x01\n\x16FieldTypeOptionContext\x12\x17\n\x07grid_i\ - d\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\ - \x06.FieldR\tgridField\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\ - \x0etypeOptionData\"v\n\x13FieldTypeOptionData\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.\ - FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etype\ - OptionData\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\ - \x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\ - \x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06ro\ - w_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\ - \tR\x07blockId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\"\xb8\ - \x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_b\ - y_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFiel\ - dId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellBy\ - FieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05va\ - lue\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedR\ - ow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Re\ - peatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\ - \x05items\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\ - \tR\x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ - Orders\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.Row\ - OrderR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\ - \x0e\n\x0cone_of_index\"Q\n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\ - \x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\ - \x0b2\x04.RowR\x03row\"\xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08bloc\ - k_id\x18\x01\x20\x01(\tR\x07blockId\x123\n\rinserted_rows\x18\x02\x20\ - \x03(\x0b2\x0e.IndexRowOrderR\x0cinsertedRows\x12,\n\x0cdeleted_rows\x18\ - \x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\ - \x04\x20\x03(\x0b2\x10.UpdatedRowOrderR\x0bupdatedRows\"E\n\tGridBlock\ - \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\ - \x20\x03(\x0b2\t.RowOrderR\trowOrders\"O\n\x04Cell\x12\x19\n\x08field_id\ - \x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\ - \x07content\x12\x12\n\x04data\x18\x03\x20\x01(\tR\x04data\"+\n\x0cRepeat\ - edCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\ - \x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\ - \x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\ - \x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10\ - CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\ - \"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of\ - _start_row_id\"\xb6\x01\n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.\ - FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etype\ - OptionData\x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartField\ - IdB\x17\n\x15one_of_start_field_id\"|\n\x1cUpdateFieldTypeOptionPayload\ - \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_i\ - d\x18\x02\x20\x01(\tR\x07fieldId\x12(\n\x10type_option_data\x18\x03\x20\ - \x01(\x0cR\x0etypeOptionData\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid\ - _id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01\ - (\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPay\ - load\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_\ - orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\ - \x03\n\x15FieldChangesetPayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\t\ - R\x07fieldId\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\ - \n\x04name\x18\x03\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\ - \x01(\tH\x01R\x04desc\x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTy\ - peH\x02R\tfieldType\x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06fr\ - ozen\x12\x20\n\nvisibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\ - \x16\n\x05width\x18\x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_optio\ - n_data\x18\t\x20\x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\ - \n\x0bone_of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\ - \n\x11one_of_visibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_opt\ - ion_data\"\x9c\x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\ - \x01(\tR\x06gridId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\ - \x12\x1d\n\nfrom_index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_\ - index\x18\x04\x20\x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\ - \x0e2\r.MoveItemTypeR\x02ty\"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid\ - _id\x18\x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\t\ - R\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\ - \x16cell_content_changeset\x18\x04\x20\x01(\tH\0R\x14cellContentChangese\ - tB\x1f\n\x1done_of_cell_content_changeset**\n\x0cMoveItemType\x12\r\n\tM\ - oveField\x10\0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08\ - RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\ - \x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\ - \x0c\n\x08Checkbox\x10\x05b\x06proto3\ + \xa0\x01\n\x10EditFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ + \x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fieldId\x12)\n\n\ + field_type\x18\x03\x20\x01(\x0e2\n.FieldTypeR\tfieldType\x12-\n\x13creat\ + e_if_not_exist\x18\x04\x20\x01(\x08R\x10createIfNotExist\"\x82\x01\n\x16\ + FieldTypeOptionContext\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridI\ + d\x12%\n\ngrid_field\x18\x02\x20\x01(\x0b2\x06.FieldR\tgridField\x12(\n\ + \x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"v\n\x13Fie\ + ldTypeOptionData\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\ + \x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type\ + _option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\ + \x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12Re\ + peatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\ + \x05items\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05ro\ + wId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\x12\x16\n\x06he\ + ight\x18\x03\x20\x01(\x05R\x06height\"\xb8\x01\n\x03Row\x12\x0e\n\x02id\ + \x18\x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_field_id\x18\x02\x20\x03(\ + \x0b2\x17.Row.CellByFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\ + \x03\x20\x01(\x05R\x06height\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\x03k\ + ey\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\ + \x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\ + \x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\ + \x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"U\n\x0eGridBlockO\ + rder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12(\n\nrow_or\ + ders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\"_\n\rIndexRowOrder\ + \x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\ + \n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone_of_index\"Q\ + \n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrder\ + R\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\x0b2\x04.RowR\x03row\"\ + \xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\t\ + R\x07blockId\x123\n\rinserted_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrd\ + erR\x0cinsertedRows\x12,\n\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOr\ + derR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\x10.Upd\ + atedRowOrderR\x0bupdatedRows\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\ + \x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ + Orders\"O\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\ + \x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\x12\x12\n\x04data\ + \x18\x03\x20\x01(\tR\x04data\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\ + \x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\ + \n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05va\ + lue\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\ + \x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid\ + _id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\ + \x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12Ins\ + ertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\ + \x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type\ + _option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_fie\ + ld_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_fiel\ + d_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\x07grid_id\x18\x01\ + \x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fiel\ + dId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"\ + d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gri\ + dId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\ + \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\ + \x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetPayload\ + \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\ + id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\x01(\tH\0\ + R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfie\ + ld_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\x12\x18\n\x06\ + frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvisibility\x18\ + \x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\x08\x20\x01(\ + \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\ + \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\ + _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\ + \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\ + emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\ + \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\ + \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\ + \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\ + \"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06\ + gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08fie\ + ld_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content_changeset\ + \x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_of_cell_co\ + ntent_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\x12\x0b\n\ + \x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\ + \x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSele\ + ct\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\ + \x05b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index 99024d3841..a2d74e96a3 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -35,8 +35,9 @@ message GetEditFieldContextPayload { } message EditFieldPayload { string grid_id = 1; - oneof one_of_field_id { string field_id = 2; }; + string field_id = 2; FieldType field_type = 3; + bool create_if_not_exist = 4; } message FieldTypeOptionContext { string grid_id = 1; From e8540b4b789ef55408c56322278565782da346f2 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 19 May 2022 10:53:11 +0800 Subject: [PATCH 08/82] chore: fix potential issue when call notifier after it was disposed --- .../grid/cell/cell_service/data_cache.dart | 2 + .../workspace/application/grid/grid_bloc.dart | 2 +- .../application/grid/grid_header_bloc.dart | 2 +- .../application/grid/grid_service.dart | 43 ++++++++++--------- .../grid/setting/property_bloc.dart | 2 +- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_cache.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_cache.dart index 409c236881..5e09573506 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_cache.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_cache.dart @@ -104,6 +104,8 @@ class GridCellCache { } Future dispose() async { + _fieldListenerByFieldId.clear(); + _cellDataByFieldId.clear(); fieldDelegate.dispose(); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart index 7f35d50978..357ccadcd9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -93,7 +93,7 @@ class GridBloc extends Bloc { emit(state.copyWith( grid: Some(grid), - fields: fieldCache.clonedFields, + fields: fieldCache.fields, rows: rowCache.clonedRows, loadingState: GridLoadingState.finish(left(unit)), )); diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart index 3195715413..526dda9085 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart @@ -15,7 +15,7 @@ class GridHeaderBloc extends Bloc { GridHeaderBloc({ required this.gridId, required this.fieldCache, - }) : super(GridHeaderState.initial(fieldCache.clonedFields)) { + }) : super(GridHeaderState.initial(fieldCache.fields)) { on( (event, emit) async { await event.map( diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart index 98827e6b72..4ed54c5f3b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -59,7 +59,7 @@ typedef ChangesetListener = void Function(GridFieldChangeset); class GridFieldCache { final String gridId; late final GridFieldsListener _fieldListener; - final FieldsNotifier _fieldNotifier = FieldsNotifier(); + FieldsNotifier? _fieldNotifier = FieldsNotifier(); final List _changesetListener = []; GridFieldCache({required this.gridId}) { @@ -81,15 +81,16 @@ class GridFieldCache { Future dispose() async { await _fieldListener.stop(); - _fieldNotifier.dispose(); + _fieldNotifier?.dispose(); + _fieldNotifier = null; } - UnmodifiableListView get unmodifiableFields => UnmodifiableListView(_fieldNotifier.fields); + UnmodifiableListView get unmodifiableFields => UnmodifiableListView(_fieldNotifier?.fields ?? []); - List get clonedFields => [..._fieldNotifier.fields]; + List get fields => [..._fieldNotifier?.fields ?? []]; set fields(List fields) { - _fieldNotifier.fields = [...fields]; + _fieldNotifier?.fields = [...fields]; } VoidCallback addListener( @@ -100,7 +101,7 @@ class GridFieldCache { } if (onChanged != null) { - onChanged(clonedFields); + onChanged(fields); } if (listener != null) { @@ -108,12 +109,12 @@ class GridFieldCache { } } - _fieldNotifier.addListener(f); + _fieldNotifier?.addListener(f); return f; } void removeListener(VoidCallback f) { - _fieldNotifier.removeListener(f); + _fieldNotifier?.removeListener(f); } void addChangesetListener(ChangesetListener listener) { @@ -131,43 +132,43 @@ class GridFieldCache { if (deletedFields.isEmpty) { return; } - final List fields = _fieldNotifier.fields; + final List newFields = fields; final Map deletedFieldMap = { for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder }; - fields.retainWhere((field) => (deletedFieldMap[field.id] == null)); - _fieldNotifier.fields = fields; + newFields.retainWhere((field) => (deletedFieldMap[field.id] == null)); + _fieldNotifier?.fields = newFields; } void _insertFields(List insertedFields) { if (insertedFields.isEmpty) { return; } - final List fields = _fieldNotifier.fields; + final List newFields = fields; for (final indexField in insertedFields) { - if (fields.length > indexField.index) { - fields.insert(indexField.index, indexField.field_1); + if (newFields.length > indexField.index) { + newFields.insert(indexField.index, indexField.field_1); } else { - fields.add(indexField.field_1); + newFields.add(indexField.field_1); } } - _fieldNotifier.fields = fields; + _fieldNotifier?.fields = newFields; } void _updateFields(List updatedFields) { if (updatedFields.isEmpty) { return; } - final List fields = _fieldNotifier.fields; + final List newFields = fields; for (final updatedField in updatedFields) { - final index = fields.indexWhere((field) => field.id == updatedField.id); + final index = newFields.indexWhere((field) => field.id == updatedField.id); if (index != -1) { - fields.removeAt(index); - fields.insert(index, updatedField); + newFields.removeAt(index); + newFields.insert(index, updatedField); } } - _fieldNotifier.fields = fields; + _fieldNotifier?.fields = newFields; } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart index 42d2327f5b..a88423e56d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart @@ -14,7 +14,7 @@ class GridPropertyBloc extends Bloc { GridPropertyBloc({required String gridId, required GridFieldCache fieldCache}) : _fieldCache = fieldCache, - super(GridPropertyState.initial(gridId, fieldCache.clonedFields)) { + super(GridPropertyState.initial(gridId, fieldCache.fields)) { on( (event, emit) async { await event.map( From e6ff4b5077f820dde04e35f9e465c2cba8ba8008 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Fri, 20 May 2022 15:55:27 +0200 Subject: [PATCH 09/82] feat: Export Markdown to filesystem --- .../workspace/application/doc/share_bloc.dart | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index c8fd8a2b96..a5fc84476f 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -1,3 +1,7 @@ +import 'dart:typed_data'; +import 'dart:async'; +import 'dart:io'; + import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; @@ -5,6 +9,8 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:file_saver/file_saver.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:dartz/dartz.dart'; part 'share_bloc.freezed.dart'; @@ -33,8 +39,26 @@ class DocShareBloc extends Bloc { ExportData _convertDeltaToMarkdown(ExportData value) { final result = deltaToMarkdown(value.data); value.data = result; + writeFile(result); return value; } + + Future get _localPath async { + final dir = await getApplicationDocumentsDirectory(); + return dir.path; + } + + Future get _localFile async { + final path = await _localPath; + print(view); + return File('$path/${view.name}.md'); + } + + Future writeFile(String md) async { + final file = await _localFile; + print(file); + return file.writeAsString(md); + } } @freezed From 0cc4967645e412daa590b5c33822a81e17b37a8a Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Fri, 20 May 2022 16:37:43 +0200 Subject: [PATCH 10/82] fix: removed unused imports --- .../app_flowy/lib/workspace/application/doc/share_bloc.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index a5fc84476f..cea6f724f9 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -1,7 +1,5 @@ -import 'dart:typed_data'; import 'dart:async'; import 'dart:io'; - import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; @@ -9,7 +7,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:file_saver/file_saver.dart'; import 'package:path_provider/path_provider.dart'; import 'package:dartz/dartz.dart'; part 'share_bloc.freezed.dart'; @@ -50,13 +47,11 @@ class DocShareBloc extends Bloc { Future get _localFile async { final path = await _localPath; - print(view); return File('$path/${view.name}.md'); } Future writeFile(String md) async { final file = await _localFile; - print(file); return file.writeAsString(md); } } From 7950f8170b45d2a450e56b119984916e12509376 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 21 May 2022 21:58:46 +0800 Subject: [PATCH 11/82] 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 12/82] 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 13/82] 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 14/82] 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 15/82] 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 16/82] 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 17/82] 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) } } From 2698fb43ad8832a3c99bf2859431a93e170a9b3d Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 14 May 2022 18:21:32 +0800 Subject: [PATCH 18/82] chore: upgrade to flutter 3.0 --- .../user/presentation/skip_log_in_screen.dart | 5 +- .../workspace/application/app/app_bloc.dart | 2 + .../grid/cell/select_option_editor_bloc.dart | 1 + .../application/grid/grid_service.dart | 4 +- .../home/menu/app/section/section.dart | 2 +- .../doc/src/widget/toolbar/tool_bar.dart | 4 +- .../emoji_picker/src/emoji_picker.dart | 2 +- .../widgets/float_bubble/question_bubble.dart | 5 +- .../linux/flutter/generated_plugins.cmake | 8 + .../macos/Runner.xcodeproj/project.pbxproj | 3 + frontend/app_flowy/pubspec.lock | 237 ++++++++++-------- frontend/app_flowy/pubspec.yaml | 6 +- 12 files changed, 158 insertions(+), 121 deletions(-) diff --git a/frontend/app_flowy/lib/user/presentation/skip_log_in_screen.dart b/frontend/app_flowy/lib/user/presentation/skip_log_in_screen.dart index bad8000643..4a78815beb 100644 --- a/frontend/app_flowy/lib/user/presentation/skip_log_in_screen.dart +++ b/frontend/app_flowy/lib/user/presentation/skip_log_in_screen.dart @@ -88,8 +88,9 @@ class _SkipLogInScreenState extends State { } _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); + final uri = Uri.parse(url); + if (await canLaunchUrl(uri)) { + await launchUrl(uri); } else { throw 'Could not launch $url'; } diff --git a/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart b/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart index 663e630a6a..e5eabe98e4 100644 --- a/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:app_flowy/plugin/plugin.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/app/app_listener.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart index 2702e6006f..a1e1315682 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart @@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; import 'select_option_service.dart'; +import 'package:collection/collection.dart'; part 'select_option_editor_bloc.freezed.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart index 4ed54c5f3b..38ebfc63fc 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; @@ -6,8 +8,6 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter/foundation.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - import 'cell/cell_service/cell_service.dart'; import 'row/row_service.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart index d9fd618e9a..d352db6620 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/section.dart @@ -26,7 +26,7 @@ class ViewSection extends StatelessWidget { listenWhen: (p, c) => p.selectedView != c.selectedView, listener: (context, state) { if (state.selectedView != null) { - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { getIt().setPlugin(state.selectedView!.plugin()); }); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/tool_bar.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/tool_bar.dart index 2ab0b78cc9..8dae41f986 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/tool_bar.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/tool_bar.dart @@ -184,7 +184,7 @@ class _ToolbarButtonListState extends State with WidgetsBindi // Listening to the WidgetsBinding instance is necessary so that we can // hide the arrows when the window gets a new size and thus the toolbar // becomes scrollable/unscrollable. - WidgetsBinding.instance!.addObserver(this); + WidgetsBinding.instance.addObserver(this); // Workaround to allow the scroll controller attach to our ListView so that // we can detect if overflow arrows need to be shown on init. @@ -226,7 +226,7 @@ class _ToolbarButtonListState extends State with WidgetsBindi @override void dispose() { _controller.dispose(); - WidgetsBinding.instance!.removeObserver(this); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart index f1e4420252..650cd185f1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart @@ -135,7 +135,7 @@ class _EmojiPickerState extends State { if (!loaded) { // Load emojis updateEmojiFuture.then( - (value) => WidgetsBinding.instance!.addPostFrameCallback((_) { + (value) => WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; setState(() { loaded = true; diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index 91ce8d07ec..762a978693 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -62,8 +62,9 @@ class QuestionBubble extends StatelessWidget { } _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); + final uri = Uri.parse(url); + if (await canLaunchUrl(uri)) { + await launchUrl(uri); } else { throw 'Could not launch $url'; } diff --git a/frontend/app_flowy/linux/flutter/generated_plugins.cmake b/frontend/app_flowy/linux/flutter/generated_plugins.cmake index 5562f19113..c7ae414da2 100644 --- a/frontend/app_flowy/linux/flutter/generated_plugins.cmake +++ b/frontend/app_flowy/linux/flutter/generated_plugins.cmake @@ -8,6 +8,9 @@ list(APPEND FLUTTER_PLUGIN_LIST window_size ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -16,3 +19,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj index 08e2493683..2e7ab66fee 100644 --- a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj +++ b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj @@ -421,6 +421,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + EXCLUDED_ARCHS = arm64; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -552,6 +553,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + EXCLUDED_ARCHS = arm64; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -575,6 +577,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + EXCLUDED_ARCHS = arm64; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index 8f7e6d6db2..65d66c67a5 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -7,28 +7,28 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "31.0.0" + version: "38.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.8.0" + version: "3.4.1" animations: dependency: transitive description: name: animations url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.3.1" async: dependency: transitive description: @@ -42,14 +42,14 @@ packages: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "8.0.2" + version: "8.0.3" bloc_test: dependency: "direct dev" description: name: bloc_test url: "https://pub.dartlang.org" source: hosted - version: "9.0.2" + version: "9.0.3" boolean_selector: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.3.0" build_config: dependency: transitive description: @@ -77,21 +77,21 @@ packages: name: build_daemon url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.1.0" build_resolvers: dependency: transitive description: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.8" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "2.1.7" + version: "2.1.11" build_runner_core: dependency: transitive description: @@ -112,7 +112,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.1.4" + version: "8.3.2" characters: dependency: transitive description: @@ -134,13 +134,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.5" clipboard: dependency: "direct main" description: @@ -168,7 +161,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" connectivity_plus: dependency: "direct main" description: @@ -182,14 +175,14 @@ packages: name: connectivity_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" connectivity_plus_macos: dependency: transitive description: name: connectivity_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.2.2" connectivity_plus_platform_interface: dependency: transitive description: @@ -224,21 +217,21 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.3.2" cross_file: dependency: transitive description: name: cross_file url: "https://pub.dartlang.org" source: hosted - version: "0.3.2" + version: "0.3.3+1" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" csslib: dependency: transitive description: @@ -259,7 +252,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.2.3" dartz: dependency: transitive description: @@ -273,14 +266,14 @@ packages: name: dbus url: "https://pub.dartlang.org" source: hosted - version: "0.6.8" + version: "0.7.3" device_info_plus: dependency: "direct main" description: name: device_info_plus url: "https://pub.dartlang.org" source: hosted - version: "3.2.1" + version: "3.2.3" device_info_plus_linux: dependency: transitive description: @@ -294,7 +287,7 @@ packages: name: device_info_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.2.3" device_info_plus_platform_interface: dependency: transitive description: @@ -329,7 +322,7 @@ packages: name: easy_localization url: "https://pub.dartlang.org" source: hosted - version: "3.0.1-dev" + version: "3.0.1" easy_logger: dependency: transitive description: @@ -357,14 +350,14 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" ffi: dependency: transitive description: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.2.1" file: dependency: transitive description: @@ -378,7 +371,7 @@ packages: name: fixnum url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" flowy_infra: dependency: "direct main" description: @@ -439,14 +432,14 @@ packages: name: flutter_inappwebview url: "https://pub.dartlang.org" source: hosted - version: "5.3.2" + version: "5.4.3+7" flutter_keyboard_visibility: dependency: transitive description: name: flutter_keyboard_visibility url: "https://pub.dartlang.org" source: hosted - version: "5.1.1" + version: "5.2.0" flutter_keyboard_visibility_platform_interface: dependency: transitive description: @@ -479,13 +472,13 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" flutter_quill: dependency: "direct main" description: path: "." - ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1 - resolved-ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1 + ref: "306fd78b7a134abdde0fed6be67f59e8a6068509" + resolved-ref: "306fd78b7a134abdde0fed6be67f59e8a6068509" url: "https://github.com/appflowy/flutter-quill.git" source: git version: "2.0.13" @@ -519,21 +512,21 @@ packages: name: freezed url: "https://pub.dartlang.org" source: hosted - version: "0.14.5" + version: "2.0.3+1" freezed_annotation: dependency: "direct main" description: name: freezed_annotation url: "https://pub.dartlang.org" source: hosted - version: "0.14.3" + version: "2.0.3" frontend_server_client: dependency: transitive description: name: frontend_server_client url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "2.1.3" get_it: dependency: "direct main" description: @@ -582,42 +575,56 @@ packages: name: http_multi_server url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.2.0" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.1" i18n_extension: dependency: transitive description: name: i18n_extension url: "https://pub.dartlang.org" source: hosted - version: "4.2.0" + version: "4.2.1" image_picker: dependency: transitive description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.8.4+4" + version: "0.8.5+3" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+13" image_picker_for_web: dependency: transitive description: name: image_picker_for_web url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.8" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+5" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.4.3" + version: "2.5.0" intl: dependency: "direct main" description: @@ -645,14 +652,14 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" json_annotation: dependency: transitive description: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "4.5.0" linked_scroll_controller: dependency: "direct main" description: @@ -666,7 +673,7 @@ packages: name: lint url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" lints: dependency: transitive description: @@ -680,7 +687,7 @@ packages: name: loading_indicator url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.0" logger: dependency: transitive description: @@ -708,7 +715,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -722,14 +729,14 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" mocktail: dependency: transitive description: name: mocktail url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.3.0" nested: dependency: transitive description: @@ -743,7 +750,7 @@ packages: name: nm url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.5.0" node_preamble: dependency: transitive description: @@ -764,14 +771,14 @@ packages: name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.4.2" package_info_plus_linux: dependency: transitive description: name: package_info_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.5" package_info_plus_macos: dependency: transitive description: @@ -792,21 +799,21 @@ packages: name: package_info_plus_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" package_info_plus_windows: dependency: transitive description: name: package_info_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_drawing: dependency: transitive description: @@ -827,49 +834,49 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.10" path_provider_android: dependency: transitive description: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.14" path_provider_ios: dependency: transitive description: name: path_provider_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.9" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.6" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" pedantic: dependency: transitive description: @@ -883,7 +890,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "5.0.0" photo_view: dependency: transitive description: @@ -932,14 +939,14 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "6.0.2" + version: "6.0.3" pub_semver: dependency: transitive description: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" pubspec_parse: dependency: transitive description: @@ -953,49 +960,49 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "3.0.1+1" + version: "3.1.0" reorderables: dependency: "direct main" description: name: reorderables url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.5.0" shared_preferences: dependency: transitive description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.15" shared_preferences_android: dependency: transitive description: name: shared_preferences_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.10" + version: "2.0.12" shared_preferences_ios: dependency: transitive description: name: shared_preferences_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.9" + version: "2.1.1" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.1.1" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.4" shared_preferences_platform_interface: dependency: transitive description: @@ -1009,21 +1016,21 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.1.1" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" shelf_packages_handler: dependency: transitive description: @@ -1070,7 +1077,7 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.2.2" source_map_stack_trace: dependency: transitive description: @@ -1091,7 +1098,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" sprintf: dependency: transitive description: @@ -1161,28 +1168,28 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.5" + version: "1.21.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.13" textfield_tags: dependency: "direct main" description: name: textfield_tags url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.0+1" textstyle_extensions: dependency: transitive description: @@ -1217,7 +1224,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" universal_platform: dependency: transitive description: @@ -1231,35 +1238,35 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.18" + version: "6.1.2" url_launcher_android: dependency: transitive description: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.14" + version: "6.0.17" url_launcher_ios: dependency: transitive description: name: url_launcher_ios url: "https://pub.dartlang.org" source: hosted - version: "6.0.14" + version: "6.0.17" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" url_launcher_platform_interface: dependency: transitive description: @@ -1273,56 +1280,70 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.11" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.1" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.5" + version: "3.0.6" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" video_player: dependency: transitive description: name: video_player url: "https://pub.dartlang.org" source: hosted - version: "2.2.11" + version: "2.4.2" + video_player_android: + dependency: transitive + description: + name: video_player_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.4" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.4" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "5.0.1" + version: "5.1.2" video_player_web: dependency: transitive description: name: video_player_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.10" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "7.5.0" + version: "8.3.0" watcher: dependency: transitive description: @@ -1336,21 +1357,21 @@ packages: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.6" + version: "2.6.1" window_size: dependency: "direct main" description: @@ -1366,28 +1387,28 @@ packages: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.0+1" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.3.1" + version: "5.4.1" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.1" youtube_player_flutter: dependency: transitive description: name: youtube_player_flutter url: "https://pub.dartlang.org" source: hosted - version: "8.0.0" + version: "8.1.0" sdks: - dart: ">=2.15.0-116.0.dev <3.0.0" - flutter: ">=2.5.0" + dart: ">=2.17.1 <3.0.0" + flutter: ">=3.0.0" diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index fd2b75b9e0..f06eedf614 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.15.0-116.0.dev <3.0.0" + sdk: ">=2.17.1 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -40,7 +40,7 @@ dependencies: flutter_quill: git: url: https://github.com/appflowy/flutter-quill.git - ref: dbc309f5e382963fa0122409a0aaf8e1417d51c1 + ref: 306fd78b7a134abdde0fed6be67f59e8a6068509 # third party packages intl: ^0.17.0 @@ -74,7 +74,7 @@ dependencies: device_info_plus: ^3.2.1 fluttertoast: ^8.0.8 table_calendar: ^3.0.5 - reorderables: + reorderables: ^0.5.0 linked_scroll_controller: ^0.2.0 dev_dependencies: From ef2e9cb57ee87b0e0250f061898f643d2c57ec81 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 25 May 2022 16:10:47 +0800 Subject: [PATCH 19/82] chore: update flutter version in ci --- .github/workflows/dart_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart_lint.yml b/.github/workflows/dart_lint.yml index 81460cec1c..ca0536906e 100644 --- a/.github/workflows/dart_lint.yml +++ b/.github/workflows/dart_lint.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v2 - uses: subosito/flutter-action@v1 with: - flutter-version: '2.10.0' + flutter-version: '3.0.0' channel: "stable" - name: Deps Flutter run: flutter packages pub get From 896123860dd45bfa652226d3e4805f4c8c818de3 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 25 May 2022 16:18:47 +0800 Subject: [PATCH 20/82] chore: update flutter sdk version --- .github/workflows/dart_test.yml | 1 + frontend/app_flowy/pubspec.lock | 2 +- frontend/app_flowy/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dart_test.yml b/.github/workflows/dart_test.yml index db479d2905..9938f02091 100644 --- a/.github/workflows/dart_test.yml +++ b/.github/workflows/dart_test.yml @@ -25,6 +25,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: 'stable' + flutter-version: '3.0.0' cache: true - name: Cache Cargo diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index 65d66c67a5..958debd9dd 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -1410,5 +1410,5 @@ packages: source: hosted version: "8.1.0" sdks: - dart: ">=2.17.1 <3.0.0" + dart: ">=2.17.0 <3.0.0" flutter: ">=3.0.0" diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index f06eedf614..eb7b17dc21 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.17.1 <3.0.0" + sdk: ">=2.16.2 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions From aad33f6746ac916134099891a5dbc2bcbeaecc91 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 25 May 2022 17:01:34 +0800 Subject: [PATCH 21/82] chore: update flutter toolchain version to flutter 3.0 --- .github/workflows/ci.yaml | 1 + .github/workflows/release.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 40f51c8132..ab1becf24f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,7 @@ jobs: with: channel: 'stable' cache: true + flutter-version: '3.0.0' - name: Cache Cargo uses: actions/cache@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93dedaa43d..4d500c4659 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: 'stable' + flutter-version: '3.0.0' - name: Pre build working-directory: frontend @@ -98,6 +99,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: 'stable' + flutter-version: '3.0.0' - name: Pre build working-directory: frontend From 89ff8517fa485707c2ee71d343fc46997b9659aa Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 26 May 2022 15:58:13 +0800 Subject: [PATCH 22/82] chore: udpate launch task --- frontend/app_flowy/.vscode/launch.json | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/frontend/app_flowy/.vscode/launch.json b/frontend/app_flowy/.vscode/launch.json index b271a1e6a1..e5ea3cdf6f 100644 --- a/frontend/app_flowy/.vscode/launch.json +++ b/frontend/app_flowy/.vscode/launch.json @@ -5,18 +5,30 @@ "version": "0.2.0", "configurations": [ { - "name": "app_flowy", + // This task builds the Rust and Dart code of AppFlowy. + "name": "Build", "request": "launch", "program": "${workspaceRoot}/lib/main.dart", - "type": "dart", "preLaunchTask": "build_flowy_sdk", + "type": "dart", "env": { "RUST_LOG": "debug" }, "cwd": "${workspaceRoot}" }, { - "name": "app_flowy(trace)", + // This task only build the Dart code of AppFlowy. + "name": "Build (Dart)", + "request": "launch", + "program": "${workspaceRoot}/lib/main.dart", + "type": "dart", + "env": { + "RUST_LOG": "debug" + }, + "cwd": "${workspaceRoot}" + }, + { + "name": "Build (trace log)", "request": "launch", "program": "${workspaceRoot}/lib/main.dart", "type": "dart", @@ -27,7 +39,7 @@ "cwd": "${workspaceRoot}" }, { - "name": "app_flowy (profile mode)", + "name": "Build (profile mode)", "request": "launch", "type": "dart", "flutterMode": "profile" From 8706b81ac0cb208247d939322ae208eb1b2bebc5 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 26 May 2022 17:28:44 +0800 Subject: [PATCH 23/82] refactor: rename struct --- frontend/rust-lib/flowy-folder/src/manager.rs | 9 ++- .../src/services/app/event_handler.rs | 2 +- .../src/services/folder_editor.rs | 6 +- .../src/services/persistence/mod.rs | 9 +-- .../services/persistence/version_2/v2_impl.rs | 4 +- .../src/services/view/controller.rs | 4 +- .../flowy-folder/tests/workspace/script.rs | 4 +- .../rust-lib/flowy-grid/src/event_handler.rs | 36 +++++------ frontend/rust-lib/flowy-grid/src/manager.rs | 64 ++++++------------- .../src/services/block_meta_editor.rs | 5 +- .../src/services/block_meta_manager.rs | 36 +++++------ .../flowy-grid/src/services/grid_editor.rs | 54 ++++++++-------- .../src/services/persistence/block_index.rs | 6 +- .../rust-lib/flowy-grid/tests/grid/script.rs | 6 +- .../rust-lib/flowy-text-block/src/editor.rs | 8 +-- .../rust-lib/flowy-text-block/src/manager.rs | 18 +++--- .../flowy-text-block/tests/document/script.rs | 4 +- .../src/client_grid/grid_block_meta_pad.rs | 2 +- 18 files changed, 124 insertions(+), 153 deletions(-) diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 05847f8cc1..fe9a160b39 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -4,7 +4,7 @@ use crate::{ errors::FlowyResult, event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser}, services::{ - folder_editor::ClientFolderEditor, persistence::FolderPersistence, set_current_workspace, AppController, + folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace, AppController, TrashController, ViewController, WorkspaceController, }, }; @@ -61,7 +61,7 @@ pub struct FolderManager { pub(crate) view_controller: Arc, pub(crate) trash_controller: Arc, web_socket: Arc, - folder_editor: Arc>>>, + folder_editor: Arc>>>, data_processors: ViewDataProcessorMap, } @@ -166,8 +166,7 @@ impl FolderManager { let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache)); let rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence); - let folder_editor = - ClientFolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?; + let folder_editor = FolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?; *self.folder_editor.write().await = Some(Arc::new(folder_editor)); let _ = self.app_controller.initialize()?; @@ -228,7 +227,7 @@ impl DefaultFolderBuilder { #[cfg(feature = "flowy_unit_test")] impl FolderManager { - pub async fn folder_editor(&self) -> Arc { + pub async fn folder_editor(&self) -> Arc { self.folder_editor.read().await.clone().unwrap() } } diff --git a/frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs b/frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs index 45034646b0..b95b91dc2e 100644 --- a/frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs +++ b/frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs @@ -46,7 +46,7 @@ pub(crate) async fn update_app_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, app_controller, view_controller))] +#[tracing::instrument(level = "trace", skip(data, app_controller, view_controller))] pub(crate) async fn read_app_handler( data: Data, app_controller: AppData>, diff --git a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs index 6ea1a7e2ec..ef94c1d316 100644 --- a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs +++ b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs @@ -17,7 +17,7 @@ use lib_ot::core::PlainTextAttributes; use parking_lot::RwLock; use std::sync::Arc; -pub struct ClientFolderEditor { +pub struct FolderEditor { user_id: String, #[allow(dead_code)] pub(crate) folder_id: FolderId, @@ -27,7 +27,7 @@ pub struct ClientFolderEditor { ws_manager: Arc, } -impl ClientFolderEditor { +impl FolderEditor { #[allow(unused_variables)] pub async fn new( user_id: &str, @@ -129,7 +129,7 @@ impl RevisionCloudService for FolderRevisionCloudService { } #[cfg(feature = "flowy_unit_test")] -impl ClientFolderEditor { +impl FolderEditor { pub fn rev_manager(&self) -> Arc { self.rev_manager.clone() } diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs index b272168e33..d5bbb3a277 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs @@ -5,7 +5,7 @@ mod version_2; use crate::{ event_map::WorkspaceDatabase, manager::FolderId, - services::{folder_editor::ClientFolderEditor, persistence::migration::FolderMigration}, + services::{folder_editor::FolderEditor, persistence::migration::FolderMigration}, }; use flowy_database::ConnectionPool; use flowy_error::{FlowyError, FlowyResult}; @@ -50,14 +50,11 @@ pub trait FolderPersistenceTransaction { pub struct FolderPersistence { database: Arc, - folder_editor: Arc>>>, + folder_editor: Arc>>>, } impl FolderPersistence { - pub fn new( - database: Arc, - folder_editor: Arc>>>, - ) -> Self { + pub fn new(database: Arc, folder_editor: Arc>>>) -> Self { Self { database, folder_editor, diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs index 8dafda3c93..40f72e10b0 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs @@ -1,5 +1,5 @@ use crate::services::{ - folder_editor::ClientFolderEditor, + folder_editor::FolderEditor, persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset}, }; use flowy_error::{FlowyError, FlowyResult}; @@ -11,7 +11,7 @@ use flowy_folder_data_model::entities::{ }; use std::sync::Arc; -impl FolderPersistenceTransaction for ClientFolderEditor { +impl FolderPersistenceTransaction for FolderEditor { fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> { if let Some(change) = self.folder.write().create_workspace(workspace)? { let _ = self.apply_change(change)?; diff --git a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs index 603b76ec2c..ae88073afc 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs @@ -129,7 +129,7 @@ impl ViewController { .await } - #[tracing::instrument(level = "debug", skip(self), err)] + #[tracing::instrument(level = "trace", skip(self), err)] pub(crate) fn set_latest_view(&self, view_id: &str) -> Result<(), FlowyError> { KV::set_str(LATEST_VIEW_ID, view_id.to_owned()); Ok(()) @@ -193,7 +193,7 @@ impl ViewController { } // belong_to_id will be the app_id or view_id. - #[tracing::instrument(level = "debug", skip(self), err)] + #[tracing::instrument(level = "trace", skip(self), err)] pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result { self.persistence .begin_transaction(|transaction| { diff --git a/frontend/rust-lib/flowy-folder/tests/workspace/script.rs b/frontend/rust-lib/flowy-folder/tests/workspace/script.rs index a6fd1fbe29..39f023ca7d 100644 --- a/frontend/rust-lib/flowy-folder/tests/workspace/script.rs +++ b/frontend/rust-lib/flowy-folder/tests/workspace/script.rs @@ -1,5 +1,5 @@ use flowy_folder::event_map::FolderEvent::*; -use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor}; +use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor}; use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId}; use flowy_folder_data_model::entities::workspace::WorkspaceId; use flowy_folder_data_model::entities::{ @@ -125,7 +125,7 @@ impl FolderTest { pub async fn run_script(&mut self, script: FolderScript) { let sdk = &self.sdk; - let folder_editor: Arc = sdk.folder_manager.folder_editor().await; + let folder_editor: Arc = sdk.folder_manager.folder_editor().await; let rev_manager = folder_editor.rev_manager(); let cache = rev_manager.revision_cache().await; diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 44123a45d9..cf5c095ee9 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -7,7 +7,7 @@ use flowy_grid_data_model::entities::*; use lib_dispatch::prelude::{data_result, AppData, Data, DataResult}; use std::sync::Arc; -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_grid_data_handler( data: Data, manager: AppData>, @@ -34,7 +34,7 @@ pub(crate) async fn get_grid_blocks_handler( data_result(repeated_grid_block) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_fields_handler( data: Data, manager: AppData>, @@ -47,7 +47,7 @@ pub(crate) async fn get_fields_handler( data_result(repeated_field) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn update_field_handler( data: Data, manager: AppData>, @@ -58,7 +58,7 @@ pub(crate) async fn update_field_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn insert_field_handler( data: Data, manager: AppData>, @@ -69,7 +69,7 @@ pub(crate) async fn insert_field_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn update_field_type_option_handler( data: Data, manager: AppData>, @@ -82,7 +82,7 @@ pub(crate) async fn update_field_type_option_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn delete_field_handler( data: Data, manager: AppData>, @@ -93,7 +93,7 @@ pub(crate) async fn delete_field_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn switch_to_field_handler( data: Data, manager: AppData>, @@ -120,7 +120,7 @@ pub(crate) async fn switch_to_field_handler( data_result(data) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn duplicate_field_handler( data: Data, manager: AppData>, @@ -132,7 +132,7 @@ pub(crate) async fn duplicate_field_handler( } /// Return the FieldTypeOptionData if the Field exists otherwise return record not found error. -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_field_type_option_data_handler( data: Data, manager: AppData>, @@ -154,7 +154,7 @@ pub(crate) async fn get_field_type_option_data_handler( } /// Create FieldMeta and save it. Return the FieldTypeOptionData. -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn create_field_type_option_data_handler( data: Data, manager: AppData>, @@ -171,7 +171,7 @@ pub(crate) async fn create_field_type_option_data_handler( }) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn move_item_handler( data: Data, manager: AppData>, @@ -252,7 +252,7 @@ pub(crate) async fn get_cell_handler( } } -#[tracing::instrument(level = "debug", skip_all, err)] +#[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn update_cell_handler( data: Data, manager: AppData>, @@ -263,7 +263,7 @@ pub(crate) async fn update_cell_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_date_cell_data_handler( data: Data, manager: AppData>, @@ -284,7 +284,7 @@ pub(crate) async fn get_date_cell_data_handler( } } -#[tracing::instrument(level = "debug", skip_all, err)] +#[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn new_select_option_handler( data: Data, manager: AppData>, @@ -301,7 +301,7 @@ pub(crate) async fn new_select_option_handler( } } -#[tracing::instrument(level = "debug", skip_all, err)] +#[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn update_select_option_handler( data: Data, manager: AppData>, @@ -341,7 +341,7 @@ pub(crate) async fn update_select_option_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_select_option_handler( data: Data, manager: AppData>, @@ -362,7 +362,7 @@ pub(crate) async fn get_select_option_handler( } } -#[tracing::instrument(level = "debug", skip_all, err)] +#[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn update_select_option_cell_handler( data: Data, manager: AppData>, @@ -373,7 +373,7 @@ pub(crate) async fn update_select_option_cell_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip_all, err)] +#[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn update_date_cell_handler( data: Data, manager: AppData>, diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 218dff8782..f8ea9e70ae 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -1,5 +1,5 @@ -use crate::services::grid_editor::ClientGridEditor; -use crate::services::persistence::block_index::BlockIndexPersistence; +use crate::services::grid_editor::GridMetaEditor; +use crate::services::persistence::block_index::BlockIndexCache; use crate::services::persistence::kv::GridKVPersistence; use crate::services::persistence::GridDatabase; use bytes::Bytes; @@ -20,9 +20,9 @@ pub trait GridUser: Send + Sync { } pub struct GridManager { - editor_map: Arc, + editor_map: Arc>>, grid_user: Arc, - block_index_persistence: Arc, + block_index_cache: Arc, #[allow(dead_code)] kv_persistence: Arc, } @@ -33,14 +33,14 @@ impl GridManager { _rev_web_socket: Arc, database: Arc, ) -> Self { - let grid_editors = Arc::new(GridEditorMap::new()); + let grid_editors = Arc::new(DashMap::new()); let kv_persistence = Arc::new(GridKVPersistence::new(database.clone())); - let block_index_persistence = Arc::new(BlockIndexPersistence::new(database)); + let block_index_persistence = Arc::new(BlockIndexCache::new(database)); Self { editor_map: grid_editors, grid_user, kv_persistence, - block_index_persistence, + block_index_cache: block_index_persistence, } } @@ -67,7 +67,7 @@ impl GridManager { } #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)] - pub async fn open_grid>(&self, grid_id: T) -> FlowyResult> { + pub async fn open_grid>(&self, grid_id: T) -> FlowyResult> { let grid_id = grid_id.as_ref(); tracing::Span::current().record("grid_id", &grid_id); self.get_or_create_grid_editor(grid_id).await @@ -90,23 +90,27 @@ impl GridManager { } // #[tracing::instrument(level = "debug", skip(self), err)] - pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult> { + pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult> { match self.editor_map.get(grid_id) { None => Err(FlowyError::internal().context("Should call open_grid function first")), - Some(editor) => Ok(editor), + Some(editor) => Ok(editor.clone()), } } - async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult> { + async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult> { match self.editor_map.get(grid_id) { None => { tracing::trace!("Create grid editor with id: {}", grid_id); let db_pool = self.grid_user.db_pool()?; let editor = self.make_grid_editor(grid_id, db_pool).await?; - self.editor_map.insert(grid_id, &editor); + + if self.editor_map.contains_key(grid_id) { + tracing::warn!("Grid:{} already exists in cache", grid_id); + } + self.editor_map.insert(grid_id.to_string(), editor.clone()); Ok(editor) } - Some(editor) => Ok(editor), + Some(editor) => Ok(editor.clone()), } } @@ -115,11 +119,10 @@ impl GridManager { &self, grid_id: &str, pool: Arc, - ) -> Result, FlowyError> { + ) -> Result, FlowyError> { let user = self.grid_user.clone(); let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?; - let grid_editor = - ClientGridEditor::new(grid_id, user, rev_manager, self.block_index_persistence.clone()).await?; + let grid_editor = GridMetaEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?; Ok(grid_editor) } @@ -145,31 +148,6 @@ impl GridManager { } } -pub struct GridEditorMap { - inner: DashMap>, -} - -impl GridEditorMap { - fn new() -> Self { - Self { inner: DashMap::new() } - } - - pub(crate) fn insert(&self, grid_id: &str, grid_editor: &Arc) { - if self.inner.contains_key(grid_id) { - tracing::warn!("Grid:{} already exists in cache", grid_id); - } - self.inner.insert(grid_id.to_string(), grid_editor.clone()); - } - - pub(crate) fn get(&self, grid_id: &str) -> Option> { - Some(self.inner.get(grid_id)?.clone()) - } - - pub(crate) fn remove(&self, grid_id: &str) { - self.inner.remove(grid_id); - } -} - pub async fn make_grid_view_data( user_id: &str, view_id: &str, @@ -192,9 +170,7 @@ pub async fn make_grid_view_data( // Indexing the block's rows build_context.block_meta_data.rows.iter().for_each(|row| { - let _ = grid_manager - .block_index_persistence - .insert_or_update(&row.block_id, &row.id); + let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id); }); // Create grid's block diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs index ce7c5c8e8d..d1cb847b37 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs @@ -8,18 +8,17 @@ use flowy_sync::util::make_delta_from_revisions; use lib_infra::future::FutureResult; use lib_ot::core::PlainTextAttributes; use std::borrow::Cow; - use std::sync::Arc; use tokio::sync::RwLock; -pub struct ClientGridBlockMetaEditor { +pub struct GridBlockMetaEditor { user_id: String, pub block_id: String, pad: Arc>, rev_manager: Arc, } -impl ClientGridBlockMetaEditor { +impl GridBlockMetaEditor { pub async fn new( user_id: &str, token: &str, diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs index 229afe75a2..6ce36488c2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs @@ -1,7 +1,7 @@ use crate::dart_notification::{send_dart_notification, GridNotification}; use crate::manager::GridUser; -use crate::services::block_meta_editor::ClientGridBlockMetaEditor; -use crate::services::persistence::block_index::BlockIndexPersistence; +use crate::services::block_meta_editor::GridBlockMetaEditor; +use crate::services::persistence::block_index::BlockIndexCache; use crate::services::row::{group_row_orders, GridBlockSnapshot}; use dashmap::DashMap; use flowy_error::FlowyResult; @@ -15,20 +15,20 @@ use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; -pub(crate) struct GridBlockMetaEditorManager { +type BlockId = String; +pub(crate) struct GridBlockManager { grid_id: String, user: Arc, - // Key: block_id - editor_map: DashMap>, - persistence: Arc, + persistence: Arc, + block_editor_map: DashMap>, } -impl GridBlockMetaEditorManager { +impl GridBlockManager { pub(crate) async fn new( grid_id: &str, user: &Arc, blocks: Vec, - persistence: Arc, + persistence: Arc, ) -> FlowyResult { let editor_map = make_block_meta_editor_map(user, blocks).await?; let user = user.clone(); @@ -36,27 +36,27 @@ impl GridBlockMetaEditorManager { let manager = Self { grid_id, user, - editor_map, + block_editor_map: editor_map, persistence, }; Ok(manager) } // #[tracing::instrument(level = "trace", skip(self))] - pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult> { + pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult> { debug_assert!(!block_id.is_empty()); - match self.editor_map.get(block_id) { + match self.block_editor_map.get(block_id) { None => { tracing::error!("The is a fatal error, block is not exist"); let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?); - self.editor_map.insert(block_id.to_owned(), editor.clone()); + self.block_editor_map.insert(block_id.to_owned(), editor.clone()); Ok(editor) } Some(editor) => Ok(editor.clone()), } } - async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult> { + async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult> { let block_id = self.persistence.get_block_id(row_id)?; Ok(self.get_editor(&block_id).await?) } @@ -67,7 +67,7 @@ impl GridBlockMetaEditorManager { row_meta: RowMeta, start_row_id: Option, ) -> FlowyResult { - let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?; + let _ = self.persistence.insert(&row_meta.block_id, &row_meta.id)?; let editor = self.get_editor(&row_meta.block_id).await?; let mut index_row_order = IndexRowOrder::from(&row_meta); @@ -90,7 +90,7 @@ impl GridBlockMetaEditorManager { let editor = self.get_editor(&block_id).await?; let mut row_count = 0; for row in row_metas { - let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?; + let _ = self.persistence.insert(&row.block_id, &row.id)?; let mut row_order = IndexRowOrder::from(&row); let (count, index) = editor.create_row(row, None).await?; row_count = count; @@ -256,7 +256,7 @@ impl GridBlockMetaEditorManager { async fn make_block_meta_editor_map( user: &Arc, blocks: Vec, -) -> FlowyResult>> { +) -> FlowyResult>> { let editor_map = DashMap::new(); for block in blocks { let editor = make_block_meta_editor(user, &block.block_id).await?; @@ -266,7 +266,7 @@ async fn make_block_meta_editor_map( Ok(editor_map) } -async fn make_block_meta_editor(user: &Arc, block_id: &str) -> FlowyResult { +async fn make_block_meta_editor(user: &Arc, block_id: &str) -> FlowyResult { let token = user.token()?; let user_id = user.user_id()?; let pool = user.db_pool()?; @@ -274,5 +274,5 @@ async fn make_block_meta_editor(user: &Arc, block_id: &str) -> Flo let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool)); let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache)); let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence); - ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await + GridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await } 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 c161e191c4..e4cee15a6e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -1,9 +1,9 @@ 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::block_meta_manager::GridBlockManager; 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::persistence::block_index::BlockIndexCache; use crate::services::row::*; use bytes::Bytes; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; @@ -19,20 +19,26 @@ use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; -pub struct ClientGridEditor { +pub struct GridMetaEditor { grid_id: String, user: Arc, grid_pad: Arc>, rev_manager: Arc, - block_meta_manager: Arc, + block_manager: Arc, } -impl ClientGridEditor { +impl Drop for GridMetaEditor { + fn drop(&mut self) { + tracing::trace!("Drop GridMetaEditor"); + } +} + +impl GridMetaEditor { pub async fn new( grid_id: &str, user: Arc, mut rev_manager: RevisionManager, - persistence: Arc, + persistence: Arc, ) -> FlowyResult> { let token = user.token()?; let cloud = Arc::new(GridRevisionCloudService { token }); @@ -41,13 +47,13 @@ impl ClientGridEditor { let grid_pad = Arc::new(RwLock::new(grid_pad)); let blocks = grid_pad.read().await.get_block_metas(); - let block_meta_manager = Arc::new(GridBlockMetaEditorManager::new(grid_id, &user, blocks, persistence).await?); + let block_meta_manager = Arc::new(GridBlockManager::new(grid_id, &user, blocks, persistence).await?); Ok(Arc::new(Self { grid_id: grid_id.to_owned(), user, grid_pad, rev_manager, - block_meta_manager, + block_manager: block_meta_manager, })) } @@ -254,10 +260,7 @@ impl ClientGridEditor { let row_order = RowOrder::from(&row_meta); // insert the row - let row_count = self - .block_meta_manager - .create_row(&block_id, row_meta, start_row_id) - .await?; + let row_count = self.block_manager.create_row(&block_id, row_meta, start_row_id).await?; // update block row count let changeset = GridBlockMetaChangeset::from_row_count(&block_id, row_count); @@ -277,7 +280,7 @@ impl ClientGridEditor { .or_insert_with(Vec::new) .push(row_meta); } - let changesets = self.block_meta_manager.insert_row(rows_by_block_id).await?; + let changesets = self.block_manager.insert_row(rows_by_block_id).await?; for changeset in changesets { let _ = self.update_block(changeset).await?; } @@ -286,7 +289,7 @@ impl ClientGridEditor { pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { let field_metas = self.get_field_metas::(None).await?; - self.block_meta_manager + self.block_manager .update_row(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta)) .await } @@ -309,7 +312,7 @@ impl ClientGridEditor { } pub async fn get_row(&self, row_id: &str) -> FlowyResult> { - match self.block_meta_manager.get_row_meta(row_id).await? { + match self.block_manager.get_row_meta(row_id).await? { None => Ok(None), Some(row_meta) => { let field_metas = self.get_field_metas::(None).await?; @@ -321,7 +324,7 @@ impl ClientGridEditor { } } pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { - let _ = self.block_meta_manager.delete_row(row_id).await?; + let _ = self.block_manager.delete_row(row_id).await?; Ok(()) } @@ -331,12 +334,12 @@ impl ClientGridEditor { pub async fn get_cell(&self, params: &CellIdentifier) -> Option { let field_meta = self.get_field_meta(¶ms.field_id).await?; - let row_meta = self.block_meta_manager.get_row_meta(¶ms.row_id).await.ok()??; + let row_meta = self.block_manager.get_row_meta(¶ms.row_id).await.ok()??; make_cell(¶ms.field_id, &field_meta, &row_meta) } pub async fn get_cell_meta(&self, row_id: &str, field_id: &str) -> FlowyResult> { - let row_meta = self.block_meta_manager.get_row_meta(row_id).await?; + let row_meta = self.block_manager.get_row_meta(row_id).await?; match row_meta { None => Ok(None), Some(row_meta) => { @@ -382,7 +385,7 @@ impl ClientGridEditor { cell_content_changeset, }; let _ = self - .block_meta_manager + .block_manager .update_cell(cell_changeset, |row_meta| { make_row_from_row_meta(&field_metas, row_meta) }) @@ -403,7 +406,7 @@ impl ClientGridEditor { } pub async fn delete_rows(&self, row_orders: Vec) -> FlowyResult<()> { - let changesets = self.block_meta_manager.delete_rows(row_orders).await?; + let changesets = self.block_manager.delete_rows(row_orders).await?; for changeset in changesets { let _ = self.update_block(changeset).await?; } @@ -415,7 +418,7 @@ impl ClientGridEditor { let field_orders = pad_read_guard.get_field_orders(); let mut block_orders = vec![]; for block_order in pad_read_guard.get_block_metas() { - let row_orders = self.block_meta_manager.get_row_orders(&block_order.block_id).await?; + let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?; let block_order = GridBlockOrder { block_id: block_order.block_id, row_orders, @@ -442,7 +445,7 @@ impl ClientGridEditor { .collect::>(), Some(block_ids) => block_ids, }; - let snapshots = self.block_meta_manager.make_block_snapshots(block_ids).await?; + let snapshots = self.block_manager.make_block_snapshots(block_ids).await?; Ok(snapshots) } @@ -476,10 +479,7 @@ impl ClientGridEditor { } pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { - let _ = self - .block_meta_manager - .move_row(row_id, from as usize, to as usize) - .await?; + let _ = self.block_manager.move_row(row_id, from as usize, to as usize).await?; Ok(()) } @@ -565,7 +565,7 @@ impl ClientGridEditor { } #[cfg(feature = "flowy_unit_test")] -impl ClientGridEditor { +impl GridMetaEditor { pub fn rev_manager(&self) -> Arc { self.rev_manager.clone() } diff --git a/frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs b/frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs index df75ec629c..c62dc502ad 100644 --- a/frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs +++ b/frontend/rust-lib/flowy-grid/src/services/persistence/block_index.rs @@ -7,11 +7,11 @@ use flowy_database::{ use flowy_error::FlowyResult; use std::sync::Arc; -pub struct BlockIndexPersistence { +pub struct BlockIndexCache { database: Arc, } -impl BlockIndexPersistence { +impl BlockIndexCache { pub fn new(database: Arc) -> Self { Self { database } } @@ -26,7 +26,7 @@ impl BlockIndexPersistence { Ok(block_id) } - pub fn insert_or_update(&self, block_id: &str, row_id: &str) -> FlowyResult<()> { + pub fn insert(&self, block_id: &str, row_id: &str) -> FlowyResult<()> { let conn = self.database.db_connection()?; let item = IndexItem { row_id: row_id.to_string(), diff --git a/frontend/rust-lib/flowy-grid/tests/grid/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/script.rs index 611ee937e2..49b6ee7c90 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/script.rs @@ -1,6 +1,6 @@ use bytes::Bytes; use flowy_grid::services::field::*; -use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder}; +use flowy_grid::services::grid_editor::{GridMetaEditor, GridPadBuilder}; use flowy_grid::services::row::CreateRowMetaPayload; use flowy_grid_data_model::entities::{ BuildGridContext, CellChangeset, Field, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, @@ -72,7 +72,7 @@ pub enum EditorScript { pub struct GridEditorTest { pub sdk: FlowySDKTest, pub grid_id: String, - pub editor: Arc, + pub editor: Arc, pub field_metas: Vec, pub grid_blocks: Vec, pub row_metas: Vec>, @@ -239,7 +239,7 @@ impl GridEditorTest { } } -async fn get_row_metas(editor: &Arc) -> Vec> { +async fn get_row_metas(editor: &Arc) -> Vec> { editor .grid_block_snapshots(None) .await diff --git a/frontend/rust-lib/flowy-text-block/src/editor.rs b/frontend/rust-lib/flowy-text-block/src/editor.rs index d43fc24f1c..c1e50536a5 100644 --- a/frontend/rust-lib/flowy-text-block/src/editor.rs +++ b/frontend/rust-lib/flowy-text-block/src/editor.rs @@ -21,7 +21,7 @@ use lib_ws::WSConnectState; use std::sync::Arc; use tokio::sync::{mpsc, oneshot}; -pub struct ClientTextBlockEditor { +pub struct TextBlockEditor { pub doc_id: String, #[allow(dead_code)] rev_manager: Arc, @@ -30,7 +30,7 @@ pub struct ClientTextBlockEditor { edit_cmd_tx: EditorCommandSender, } -impl ClientTextBlockEditor { +impl TextBlockEditor { #[allow(unused_variables)] pub(crate) async fn new( doc_id: &str, @@ -185,7 +185,7 @@ impl ClientTextBlockEditor { pub(crate) fn receive_ws_state(&self, _state: &WSConnectState) {} } -impl std::ops::Drop for ClientTextBlockEditor { +impl std::ops::Drop for TextBlockEditor { fn drop(&mut self) { tracing::trace!("{} ClientBlockEditor was dropped", self.doc_id) } @@ -204,7 +204,7 @@ fn spawn_edit_queue( } #[cfg(feature = "flowy_unit_test")] -impl ClientTextBlockEditor { +impl TextBlockEditor { pub async fn text_block_delta(&self) -> FlowyResult { let (ret, rx) = oneshot::channel::>(); let msg = EditorCommand::ReadDelta { ret }; diff --git a/frontend/rust-lib/flowy-text-block/src/manager.rs b/frontend/rust-lib/flowy-text-block/src/manager.rs index 8a34c6916d..9325fe6001 100644 --- a/frontend/rust-lib/flowy-text-block/src/manager.rs +++ b/frontend/rust-lib/flowy-text-block/src/manager.rs @@ -1,4 +1,4 @@ -use crate::{editor::ClientTextBlockEditor, errors::FlowyError, BlockCloudService}; +use crate::{editor::TextBlockEditor, errors::FlowyError, BlockCloudService}; use bytes::Bytes; use dashmap::DashMap; use flowy_database::ConnectionPool; @@ -47,8 +47,8 @@ impl TextBlockManager { Ok(()) } - #[tracing::instrument(level = "debug", skip(self, block_id), fields(block_id), err)] - pub async fn open_block>(&self, block_id: T) -> Result, FlowyError> { + #[tracing::instrument(level = "trace", skip(self, block_id), fields(block_id), err)] + pub async fn open_block>(&self, block_id: T) -> Result, FlowyError> { let block_id = block_id.as_ref(); tracing::Span::current().record("block_id", &block_id); self.get_block_editor(block_id).await @@ -108,7 +108,7 @@ impl TextBlockManager { } impl TextBlockManager { - async fn get_block_editor(&self, block_id: &str) -> FlowyResult> { + async fn get_block_editor(&self, block_id: &str) -> FlowyResult> { match self.editor_map.get(block_id) { None => { let db_pool = self.user.db_pool()?; @@ -123,7 +123,7 @@ impl TextBlockManager { &self, block_id: &str, pool: Arc, - ) -> Result, FlowyError> { + ) -> Result, FlowyError> { let user = self.user.clone(); let token = self.user.token()?; let rev_manager = self.make_rev_manager(block_id, pool.clone())?; @@ -132,7 +132,7 @@ impl TextBlockManager { server: self.cloud_service.clone(), }); let doc_editor = - ClientTextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?; + TextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?; self.editor_map.insert(block_id, &doc_editor); Ok(doc_editor) } @@ -180,7 +180,7 @@ impl RevisionCloudService for TextBlockRevisionCloudService { } pub struct TextBlockEditorMap { - inner: DashMap>, + inner: DashMap>, } impl TextBlockEditorMap { @@ -188,14 +188,14 @@ impl TextBlockEditorMap { Self { inner: DashMap::new() } } - pub(crate) fn insert(&self, block_id: &str, doc: &Arc) { + pub(crate) fn insert(&self, block_id: &str, doc: &Arc) { if self.inner.contains_key(block_id) { log::warn!("Doc:{} already exists in cache", block_id); } self.inner.insert(block_id.to_string(), doc.clone()); } - pub(crate) fn get(&self, block_id: &str) -> Option> { + pub(crate) fn get(&self, block_id: &str) -> Option> { Some(self.inner.get(block_id)?.clone()) } diff --git a/frontend/rust-lib/flowy-text-block/tests/document/script.rs b/frontend/rust-lib/flowy-text-block/tests/document/script.rs index 5511896fc2..1722724c3f 100644 --- a/frontend/rust-lib/flowy-text-block/tests/document/script.rs +++ b/frontend/rust-lib/flowy-text-block/tests/document/script.rs @@ -1,6 +1,6 @@ use flowy_revision::disk::RevisionState; use flowy_test::{helper::ViewTest, FlowySDKTest}; -use flowy_text_block::editor::ClientTextBlockEditor; +use flowy_text_block::editor::TextBlockEditor; use flowy_text_block::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS; use lib_ot::{core::Interval, rich_text::RichTextDelta}; use std::sync::Arc; @@ -19,7 +19,7 @@ pub enum EditorScript { pub struct TextBlockEditorTest { pub sdk: FlowySDKTest, - pub editor: Arc, + pub editor: Arc, } impl TextBlockEditorTest { diff --git a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs index 3f727faf59..3370719e68 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs @@ -175,7 +175,7 @@ impl GridBlockMetaPad { match cal_diff::(old, new) { None => Ok(None), Some(delta) => { - tracing::debug!("[GridBlockMeta] Composing delta {}", delta.to_delta_str()); + tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_delta_str()); // tracing::debug!( // "[GridBlockMeta] current delta: {}", // self.delta.to_str().unwrap_or_else(|_| "".to_string()) From 9a93a72c33d96d327597b6c13aa5ddc46fdeff34 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 27 May 2022 19:03:48 +0800 Subject: [PATCH 24/82] feat: add new field type --- .../assets/images/grid/field/url.svg | 3 + .../app_flowy/assets/translations/en.json | 1 + .../grid/cell/cell_service/cell_service.dart | 1 - .../cell/cell_service/context_builder.dart | 14 +- .../application/grid/cell/url_cell_bloc.dart | 72 ++++ .../grid/src/widgets/cell/cell_builder.dart | 8 +- .../grid/src/widgets/cell/url_cell.dart | 127 ++++++ .../widgets/header/field_editor_pannel.dart | 18 +- .../widgets/header/field_type_extension.dart | 10 +- .../src/widgets/header/type_option/url.dart | 20 + .../grid/src/widgets/row/row_detail.dart | 9 +- .../flowy_sdk/lib/dispatch/dispatch.dart | 1 + .../flowy-grid-data-model/grid.pbenum.dart | 2 + .../flowy-grid-data-model/grid.pbjson.dart | 3 +- .../lib/protobuf/flowy-grid/protobuf.dart | 1 + .../flowy-grid/text_type_option.pb.dart | 16 +- .../flowy-grid/text_type_option.pbjson.dart | 4 +- .../flowy-grid/url_type_option.pb.dart | 119 ++++++ .../flowy-grid/url_type_option.pbenum.dart | 7 + .../flowy-grid/url_type_option.pbjson.dart | 31 ++ .../flowy-grid/url_type_option.pbserver.dart | 9 + frontend/rust-lib/Cargo.lock | 1 + frontend/rust-lib/flowy-grid/Cargo.toml | 1 + .../rust-lib/flowy-grid/src/event_handler.rs | 6 +- .../flowy-grid/src/protobuf/model/mod.rs | 3 + .../src/protobuf/model/text_type_option.rs | 46 +- .../src/protobuf/model/url_type_option.rs | 403 ++++++++++++++++++ .../src/protobuf/proto/text_type_option.proto | 2 +- .../src/protobuf/proto/url_type_option.proto | 9 + .../src/services/field/field_builder.rs | 3 + .../src/services/field/type_options/mod.rs | 2 + .../field/type_options/number_type_option.rs | 6 +- .../field/type_options/text_type_option.rs | 2 +- .../field/type_options/url_type_option.rs | 101 +++++ .../src/services/row/cell_data_operation.rs | 4 + .../src/entities/grid.rs | 5 + .../src/protobuf/model/grid.rs | 7 +- .../src/protobuf/proto/grid.proto | 1 + 38 files changed, 1016 insertions(+), 62 deletions(-) create mode 100644 frontend/app_flowy/assets/images/grid/field/url.svg create mode 100644 frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pb.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbenum.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbjson.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbserver.dart create mode 100644 frontend/rust-lib/flowy-grid/src/protobuf/model/url_type_option.rs create mode 100644 frontend/rust-lib/flowy-grid/src/protobuf/proto/url_type_option.proto create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs diff --git a/frontend/app_flowy/assets/images/grid/field/url.svg b/frontend/app_flowy/assets/images/grid/field/url.svg new file mode 100644 index 0000000000..f00f5c7aa2 --- /dev/null +++ b/frontend/app_flowy/assets/images/grid/field/url.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 8fc9e90581..4e6c8f3420 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -160,6 +160,7 @@ "numberFieldName": "Numbers", "singleSelectFieldName": "Select", "multiSelectFieldName": "Multiselect", + "urlFieldName": "URL", "numberFormat": " Number format", "dateFormat": " Date format", "includeTime": " Include time", diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart index 07be14efa2..23b68f2f87 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart @@ -12,7 +12,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; - import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index ed191c7d60..8680c7db76 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -3,6 +3,7 @@ part of 'cell_service.dart'; typedef GridCellContext = _GridCellContext; typedef GridSelectOptionCellContext = _GridCellContext; typedef GridDateCellContext = _GridCellContext; +typedef GridURLCellContext = _GridCellContext; class GridCellContextBuilder { final GridCellCache _cellCache; @@ -58,12 +59,21 @@ class GridCellContextBuilder { cellDataLoader: SelectOptionCellDataLoader(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); - default: - throw UnimplementedError; + + case FieldType.URL: + return GridURLCellContext( + gridCell: _gridCell, + cellCache: _cellCache, + cellDataLoader: GridCellDataLoader(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + ); } + throw UnimplementedError; } } +// T: the type of the CellData +// D: the type of the data that will be save to disk // ignore: must_be_immutable class _GridCellContext extends Equatable { final GridCell gridCell; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart new file mode 100644 index 0000000000..8b4245540a --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart @@ -0,0 +1,72 @@ +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'dart:async'; +import 'cell_service/cell_service.dart'; + +part 'url_cell_bloc.freezed.dart'; + +class URLCellBloc extends Bloc { + final GridURLCellContext cellContext; + void Function()? _onCellChangedFn; + URLCellBloc({ + required this.cellContext, + }) : super(URLCellState.initial(cellContext)) { + on( + (event, emit) async { + event.when( + initial: () { + _startListening(); + }, + updateText: (text) { + cellContext.saveCellData(text); + emit(state.copyWith(content: text)); + }, + didReceiveCellUpdate: (cellData) { + emit(state.copyWith(content: cellData.content)); + }, + ); + }, + ); + } + + @override + Future close() async { + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); + return super.close(); + } + + void _startListening() { + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((cellData) { + if (!isClosed) { + add(URLCellEvent.didReceiveCellUpdate(cellData)); + } + }), + ); + } +} + +@freezed +class URLCellEvent with _$URLCellEvent { + const factory URLCellEvent.initial() = _InitialCell; + const factory URLCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; + const factory URLCellEvent.updateText(String text) = _UpdateText; +} + +@freezed +class URLCellState with _$URLCellState { + const factory URLCellState({ + required String content, + required String url, + }) = _URLCellState; + + factory URLCellState.initial(GridURLCellContext context) { + final cellData = context.getCellData(); + return URLCellState(content: cellData?.content ?? "", url: ""); + } +} 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 6f74d23ad0..8ae79541a3 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 @@ -13,6 +13,7 @@ import 'date_cell/date_cell.dart'; import 'number_cell.dart'; import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; +import 'url_cell.dart'; GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) { final key = ValueKey(gridCell.cellId()); @@ -32,10 +33,11 @@ GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, { return NumberCell(cellContextBuilder: cellContextBuilder, key: key); case FieldType.RichText: return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key); - - default: - throw UnimplementedError; + case FieldType.URL: + return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key); + } + throw UnimplementedError; } class BlankCell extends StatelessWidget { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart new file mode 100644 index 0000000000..328d0e7180 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart @@ -0,0 +1,127 @@ +import 'dart:async'; +import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/prelude.dart'; +import 'cell_builder.dart'; + +class GridURLCellStyle extends GridCellStyle { + String? placeholder; + + GridURLCellStyle({ + this.placeholder, + }); +} + +class GridURLCell extends StatefulWidget with GridCellWidget { + final GridCellContextBuilder cellContextBuilder; + late final GridURLCellStyle? cellStyle; + GridURLCell({ + required this.cellContextBuilder, + GridCellStyle? style, + Key? key, + }) : super(key: key) { + if (style != null) { + cellStyle = (style as GridURLCellStyle); + } else { + cellStyle = null; + } + } + + @override + State createState() => _GridURLCellState(); +} + +class _GridURLCellState extends State { + late URLCellBloc _cellBloc; + late TextEditingController _controller; + late CellSingleFocusNode _focusNode; + Timer? _delayOperation; + + @override + void initState() { + final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; + _cellBloc = URLCellBloc(cellContext: cellContext); + _cellBloc.add(const URLCellEvent.initial()); + _controller = TextEditingController(text: _cellBloc.state.content); + _focusNode = CellSingleFocusNode(); + + _listenFocusNode(); + _listenRequestFocus(context); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider.value( + value: _cellBloc, + child: BlocListener( + listener: (context, state) { + if (_controller.text != state.content) { + _controller.text = state.content; + } + }, + child: TextField( + controller: _controller, + focusNode: _focusNode, + onChanged: (value) => focusChanged(), + onEditingComplete: () => _focusNode.unfocus(), + maxLines: null, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + hintText: widget.cellStyle?.placeholder, + isDense: true, + ), + ), + ), + ); + } + + @override + Future dispose() async { + widget.requestFocus.removeAllListener(); + _delayOperation?.cancel(); + _cellBloc.close(); + _focusNode.removeSingleListener(); + _focusNode.dispose(); + + super.dispose(); + } + + @override + void didUpdateWidget(covariant GridURLCell oldWidget) { + if (oldWidget != widget) { + _listenFocusNode(); + } + super.didUpdateWidget(oldWidget); + } + + void _listenFocusNode() { + widget.onFocus.value = _focusNode.hasFocus; + _focusNode.setSingleListener(() { + widget.onFocus.value = _focusNode.hasFocus; + focusChanged(); + }); + } + + void _listenRequestFocus(BuildContext context) { + widget.requestFocus.addListener(() { + if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(_focusNode); + } + }); + } + + Future focusChanged() async { + if (mounted) { + _delayOperation?.cancel(); + _delayOperation = Timer(const Duration(milliseconds: 300), () { + if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { + _cellBloc.add(URLCellEvent.updateText(_controller.text)); + } + }); + } + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart index eb42267445..63b790b02c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart @@ -22,6 +22,7 @@ import 'type_option/multi_select.dart'; import 'type_option/number.dart'; import 'type_option/rich_text.dart'; import 'type_option/single_select.dart'; +import 'type_option/url.dart'; typedef UpdateFieldCallback = void Function(Field, Uint8List); typedef SwitchToFieldCallback = Future> Function( @@ -168,9 +169,12 @@ TypeOptionBuilder _makeTypeOptionBuild({ typeOptionContext as RichTextTypeOptionContext, ); - default: - throw UnimplementedError; + case FieldType.URL: + return URLTypeOptionBuilder( + typeOptionContext as URLTypeOptionContext, + ); } + throw UnimplementedError; } TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) { @@ -205,9 +209,15 @@ TypeOptionContext _makeTypeOptionContext(GridFieldContext fieldContext) { fieldContext: fieldContext, dataBuilder: SingleSelectTypeOptionDataBuilder(), ); - default: - throw UnimplementedError(); + + case FieldType.URL: + return URLTypeOptionContext( + fieldContext: fieldContext, + dataBuilder: URLTypeOptionDataBuilder(), + ); } + + throw UnimplementedError(); } abstract class TypeOptionWidget extends StatelessWidget { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart index a4da8fa1b9..035d101544 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart @@ -17,9 +17,10 @@ extension FieldTypeListExtension on FieldType { return "grid/field/text"; case FieldType.SingleSelect: return "grid/field/single_select"; - default: - throw UnimplementedError; + case FieldType.URL: + return "grid/field/url"; } + throw UnimplementedError; } String title() { @@ -36,8 +37,9 @@ extension FieldTypeListExtension on FieldType { return LocaleKeys.grid_field_textFieldName.tr(); case FieldType.SingleSelect: return LocaleKeys.grid_field_singleSelectFieldName.tr(); - default: - throw UnimplementedError; + case FieldType.URL: + return LocaleKeys.grid_field_urlFieldName.tr(); } + throw UnimplementedError; } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart new file mode 100644 index 0000000000..f4e73f7fdc --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/url.dart @@ -0,0 +1,20 @@ +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; +import 'package:flutter/material.dart'; + +typedef URLTypeOptionContext = TypeOptionContext; + +class URLTypeOptionDataBuilder extends TypeOptionDataBuilder { + @override + URLTypeOption fromBuffer(List buffer) { + return URLTypeOption.fromBuffer(buffer); + } +} + +class URLTypeOptionBuilder extends TypeOptionBuilder { + URLTypeOptionBuilder(URLTypeOptionContext typeOptionContext); + + @override + Widget? get customWidget => null; +} 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 f2b93e5018..255553f3d2 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 @@ -4,6 +4,7 @@ import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart'; import 'package:flowy_infra/image.dart'; @@ -212,7 +213,11 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { return SelectOptionCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), ); - default: - return null; + + case FieldType.URL: + return GridURLCellStyle( + placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + ); } + return null; } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart index ca3596e48a..35457ff989 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart @@ -9,6 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-net/event.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user/event_map.pb.dart'; diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart index e6cb17314b..78331a46e5 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart @@ -31,6 +31,7 @@ class FieldType extends $pb.ProtobufEnum { static const FieldType SingleSelect = FieldType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SingleSelect'); static const FieldType MultiSelect = FieldType._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MultiSelect'); static const FieldType Checkbox = FieldType._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Checkbox'); + static const FieldType URL = FieldType._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'URL'); static const $core.List values = [ RichText, @@ -39,6 +40,7 @@ class FieldType extends $pb.ProtobufEnum { SingleSelect, MultiSelect, Checkbox, + URL, ]; static final $core.Map<$core.int, FieldType> _byValue = $pb.ProtobufEnum.initByValue(values); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index 3a925cb282..c4f25d7fe3 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -29,11 +29,12 @@ const FieldType$json = const { const {'1': 'SingleSelect', '2': 3}, const {'1': 'MultiSelect', '2': 4}, const {'1': 'Checkbox', '2': 5}, + const {'1': 'URL', '2': 6}, ], }; /// Descriptor for `FieldType`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBQ=='); +final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBRIHCgNVUkwQBg=='); @$core.Deprecated('Use gridDescriptor instead') const Grid$json = const { '1': 'Grid', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart index af6583c106..c056e2799a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart @@ -5,6 +5,7 @@ export './dart_notification.pb.dart'; export './selection_type_option.pb.dart'; export './row_entities.pb.dart'; export './cell_entities.pb.dart'; +export './url_type_option.pb.dart'; export './checkbox_type_option.pb.dart'; export './event_map.pb.dart'; export './text_type_option.pb.dart'; diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pb.dart index c30f2eb6e1..a38a68be36 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pb.dart @@ -11,17 +11,17 @@ import 'package:protobuf/protobuf.dart' as $pb; class RichTextTypeOption extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RichTextTypeOption', createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format') + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data') ..hasRequiredFields = false ; RichTextTypeOption._() : super(); factory RichTextTypeOption({ - $core.String? format, + $core.String? data, }) { final _result = create(); - if (format != null) { - _result.format = format; + if (data != null) { + _result.data = data; } return _result; } @@ -47,12 +47,12 @@ class RichTextTypeOption extends $pb.GeneratedMessage { static RichTextTypeOption? _defaultInstance; @$pb.TagNumber(1) - $core.String get format => $_getSZ(0); + $core.String get data => $_getSZ(0); @$pb.TagNumber(1) - set format($core.String v) { $_setString(0, v); } + set data($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) - $core.bool hasFormat() => $_has(0); + $core.bool hasData() => $_has(0); @$pb.TagNumber(1) - void clearFormat() => clearField(1); + void clearData() => clearField(1); } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pbjson.dart index e4ba6956ee..5999ce87e0 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/text_type_option.pbjson.dart @@ -12,9 +12,9 @@ import 'dart:typed_data' as $typed_data; const RichTextTypeOption$json = const { '1': 'RichTextTypeOption', '2': const [ - const {'1': 'format', '3': 1, '4': 1, '5': 9, '10': 'format'}, + const {'1': 'data', '3': 1, '4': 1, '5': 9, '10': 'data'}, ], }; /// Descriptor for `RichTextTypeOption`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List richTextTypeOptionDescriptor = $convert.base64Decode('ChJSaWNoVGV4dFR5cGVPcHRpb24SFgoGZm9ybWF0GAEgASgJUgZmb3JtYXQ='); +final $typed_data.Uint8List richTextTypeOptionDescriptor = $convert.base64Decode('ChJSaWNoVGV4dFR5cGVPcHRpb24SEgoEZGF0YRgBIAEoCVIEZGF0YQ=='); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pb.dart new file mode 100644 index 0000000000..c43474a92a --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pb.dart @@ -0,0 +1,119 @@ +/// +// Generated code. Do not modify. +// source: url_type_option.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class URLTypeOption extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'URLTypeOption', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data') + ..hasRequiredFields = false + ; + + URLTypeOption._() : super(); + factory URLTypeOption({ + $core.String? data, + }) { + final _result = create(); + if (data != null) { + _result.data = data; + } + return _result; + } + factory URLTypeOption.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory URLTypeOption.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + URLTypeOption clone() => URLTypeOption()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + URLTypeOption copyWith(void Function(URLTypeOption) updates) => super.copyWith((message) => updates(message as URLTypeOption)) as URLTypeOption; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static URLTypeOption create() => URLTypeOption._(); + URLTypeOption createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static URLTypeOption getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static URLTypeOption? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get data => $_getSZ(0); + @$pb.TagNumber(1) + set data($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasData() => $_has(0); + @$pb.TagNumber(1) + void clearData() => clearField(1); +} + +class URLCellData extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'URLCellData', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') + ..hasRequiredFields = false + ; + + URLCellData._() : super(); + factory URLCellData({ + $core.String? url, + $core.String? content, + }) { + final _result = create(); + if (url != null) { + _result.url = url; + } + if (content != null) { + _result.content = content; + } + return _result; + } + factory URLCellData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory URLCellData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + URLCellData clone() => URLCellData()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + URLCellData copyWith(void Function(URLCellData) updates) => super.copyWith((message) => updates(message as URLCellData)) as URLCellData; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static URLCellData create() => URLCellData._(); + URLCellData createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static URLCellData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static URLCellData? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get url => $_getSZ(0); + @$pb.TagNumber(1) + set url($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasUrl() => $_has(0); + @$pb.TagNumber(1) + void clearUrl() => clearField(1); + + @$pb.TagNumber(2) + $core.String get content => $_getSZ(1); + @$pb.TagNumber(2) + set content($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasContent() => $_has(1); + @$pb.TagNumber(2) + void clearContent() => clearField(2); +} + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbenum.dart new file mode 100644 index 0000000000..de8793d432 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbenum.dart @@ -0,0 +1,7 @@ +/// +// Generated code. Do not modify. +// source: url_type_option.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbjson.dart new file mode 100644 index 0000000000..30ac81dfb2 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbjson.dart @@ -0,0 +1,31 @@ +/// +// Generated code. Do not modify. +// source: url_type_option.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +import 'dart:core' as $core; +import 'dart:convert' as $convert; +import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use uRLTypeOptionDescriptor instead') +const URLTypeOption$json = const { + '1': 'URLTypeOption', + '2': const [ + const {'1': 'data', '3': 1, '4': 1, '5': 9, '10': 'data'}, + ], +}; + +/// Descriptor for `URLTypeOption`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List uRLTypeOptionDescriptor = $convert.base64Decode('Cg1VUkxUeXBlT3B0aW9uEhIKBGRhdGEYASABKAlSBGRhdGE='); +@$core.Deprecated('Use uRLCellDataDescriptor instead') +const URLCellData$json = const { + '1': 'URLCellData', + '2': const [ + const {'1': 'url', '3': 1, '4': 1, '5': 9, '10': 'url'}, + const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, + ], +}; + +/// Descriptor for `URLCellData`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List uRLCellDataDescriptor = $convert.base64Decode('CgtVUkxDZWxsRGF0YRIQCgN1cmwYASABKAlSA3VybBIYCgdjb250ZW50GAIgASgJUgdjb250ZW50'); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbserver.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbserver.dart new file mode 100644 index 0000000000..6889e31393 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/url_type_option.pbserver.dart @@ -0,0 +1,9 @@ +/// +// Generated code. Do not modify. +// source: url_type_option.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +export 'url_type_option.pb.dart'; + diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 554def4acd..23fe8ed62b 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -953,6 +953,7 @@ dependencies = [ "strum_macros", "tokio", "tracing", + "url", ] [[package]] diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index 1691b3eee7..2bde4a3f36 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -35,6 +35,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0"} serde_repr = "0.1" indexmap = {version = "1.8.1", features = ["serde"]} +url = { version = "2"} [dev-dependencies] flowy-test = { path = "../flowy-test" } diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 44123a45d9..eda9840747 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -263,7 +263,7 @@ pub(crate) async fn update_cell_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(data, manager), err)] +#[tracing::instrument(level = "trace", skip(data, manager), err)] pub(crate) async fn get_date_cell_data_handler( data: Data, manager: AppData>, @@ -272,7 +272,7 @@ pub(crate) async fn get_date_cell_data_handler( let editor = manager.get_grid_editor(¶ms.grid_id)?; match editor.get_field_meta(¶ms.field_id).await { None => { - tracing::error!("Can't find the corresponding field with id: {}", params.field_id); + tracing::error!("Can't find the date field with id: {}", params.field_id); data_result(DateCellData::default()) } Some(field_meta) => { @@ -350,7 +350,7 @@ pub(crate) async fn get_select_option_handler( let editor = manager.get_grid_editor(¶ms.grid_id)?; match editor.get_field_meta(¶ms.field_id).await { None => { - tracing::error!("Can't find the corresponding field with id: {}", params.field_id); + tracing::error!("Can't find the select option field with id: {}", params.field_id); data_result(SelectOptionCellData::default()) } Some(field_meta) => { diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs index 99d0ecd1b6..c0f74e1e9c 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs @@ -19,6 +19,9 @@ pub use row_entities::*; mod cell_entities; pub use cell_entities::*; +mod url_type_option; +pub use url_type_option::*; + mod checkbox_type_option; pub use checkbox_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/text_type_option.rs index febc180e03..b6bb5e55ab 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/text_type_option.rs @@ -26,7 +26,7 @@ #[derive(PartialEq,Clone,Default)] pub struct RichTextTypeOption { // message fields - pub format: ::std::string::String, + pub data: ::std::string::String, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -43,30 +43,30 @@ impl RichTextTypeOption { ::std::default::Default::default() } - // string format = 1; + // string data = 1; - pub fn get_format(&self) -> &str { - &self.format + pub fn get_data(&self) -> &str { + &self.data } - pub fn clear_format(&mut self) { - self.format.clear(); + pub fn clear_data(&mut self) { + self.data.clear(); } // Param is passed by value, moved - pub fn set_format(&mut self, v: ::std::string::String) { - self.format = v; + pub fn set_data(&mut self, v: ::std::string::String) { + self.data = v; } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_format(&mut self) -> &mut ::std::string::String { - &mut self.format + pub fn mut_data(&mut self) -> &mut ::std::string::String { + &mut self.data } // Take field - pub fn take_format(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.format, ::std::string::String::new()) + pub fn take_data(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.data, ::std::string::String::new()) } } @@ -80,7 +80,7 @@ impl ::protobuf::Message for RichTextTypeOption { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.format)?; + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; @@ -94,8 +94,8 @@ impl ::protobuf::Message for RichTextTypeOption { #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; - if !self.format.is_empty() { - my_size += ::protobuf::rt::string_size(1, &self.format); + if !self.data.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.data); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -103,8 +103,8 @@ impl ::protobuf::Message for RichTextTypeOption { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { - if !self.format.is_empty() { - os.write_string(1, &self.format)?; + if !self.data.is_empty() { + os.write_string(1, &self.data)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -145,9 +145,9 @@ impl ::protobuf::Message for RichTextTypeOption { descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "format", - |m: &RichTextTypeOption| { &m.format }, - |m: &mut RichTextTypeOption| { &mut m.format }, + "data", + |m: &RichTextTypeOption| { &m.data }, + |m: &mut RichTextTypeOption| { &mut m.data }, )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "RichTextTypeOption", @@ -165,7 +165,7 @@ impl ::protobuf::Message for RichTextTypeOption { impl ::protobuf::Clear for RichTextTypeOption { fn clear(&mut self) { - self.format.clear(); + self.data.clear(); self.unknown_fields.clear(); } } @@ -183,8 +183,8 @@ impl ::protobuf::reflect::ProtobufValue for RichTextTypeOption { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x16text_type_option.proto\",\n\x12RichTextTypeOption\x12\x16\n\x06for\ - mat\x18\x01\x20\x01(\tR\x06formatb\x06proto3\ + \n\x16text_type_option.proto\"(\n\x12RichTextTypeOption\x12\x12\n\x04dat\ + a\x18\x01\x20\x01(\tR\x04datab\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/url_type_option.rs new file mode 100644 index 0000000000..fe83999fd3 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/url_type_option.rs @@ -0,0 +1,403 @@ +// This file is generated by rust-protobuf 2.25.2. Do not edit +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `url_type_option.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2; + +#[derive(PartialEq,Clone,Default)] +pub struct URLTypeOption { + // message fields + pub data: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a URLTypeOption { + fn default() -> &'a URLTypeOption { + ::default_instance() + } +} + +impl URLTypeOption { + pub fn new() -> URLTypeOption { + ::std::default::Default::default() + } + + // string data = 1; + + + pub fn get_data(&self) -> &str { + &self.data + } + pub fn clear_data(&mut self) { + self.data.clear(); + } + + // Param is passed by value, moved + pub fn set_data(&mut self, v: ::std::string::String) { + self.data = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_data(&mut self) -> &mut ::std::string::String { + &mut self.data + } + + // Take field + pub fn take_data(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.data, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for URLTypeOption { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.data.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.data); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.data.is_empty() { + os.write_string(1, &self.data)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: ::std::boxed::Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> URLTypeOption { + URLTypeOption::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "data", + |m: &URLTypeOption| { &m.data }, + |m: &mut URLTypeOption| { &mut m.data }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "URLTypeOption", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static URLTypeOption { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(URLTypeOption::new) + } +} + +impl ::protobuf::Clear for URLTypeOption { + fn clear(&mut self) { + self.data.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for URLTypeOption { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for URLTypeOption { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] +pub struct URLCellData { + // message fields + pub url: ::std::string::String, + pub content: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a URLCellData { + fn default() -> &'a URLCellData { + ::default_instance() + } +} + +impl URLCellData { + pub fn new() -> URLCellData { + ::std::default::Default::default() + } + + // string url = 1; + + + pub fn get_url(&self) -> &str { + &self.url + } + pub fn clear_url(&mut self) { + self.url.clear(); + } + + // Param is passed by value, moved + pub fn set_url(&mut self, v: ::std::string::String) { + self.url = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_url(&mut self) -> &mut ::std::string::String { + &mut self.url + } + + // Take field + pub fn take_url(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.url, ::std::string::String::new()) + } + + // string content = 2; + + + pub fn get_content(&self) -> &str { + &self.content + } + pub fn clear_content(&mut self) { + self.content.clear(); + } + + // Param is passed by value, moved + pub fn set_content(&mut self, v: ::std::string::String) { + self.content = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_content(&mut self) -> &mut ::std::string::String { + &mut self.content + } + + // Take field + pub fn take_content(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.content, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for URLCellData { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.url)?; + }, + 2 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.url.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.url); + } + if !self.content.is_empty() { + my_size += ::protobuf::rt::string_size(2, &self.content); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.url.is_empty() { + os.write_string(1, &self.url)?; + } + if !self.content.is_empty() { + os.write_string(2, &self.content)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: ::std::boxed::Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> URLCellData { + URLCellData::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "url", + |m: &URLCellData| { &m.url }, + |m: &mut URLCellData| { &mut m.url }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "content", + |m: &URLCellData| { &m.content }, + |m: &mut URLCellData| { &mut m.content }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "URLCellData", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static URLCellData { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(URLCellData::new) + } +} + +impl ::protobuf::Clear for URLCellData { + fn clear(&mut self) { + self.url.clear(); + self.content.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for URLCellData { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for URLCellData { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x15url_type_option.proto\"#\n\rURLTypeOption\x12\x12\n\x04data\x18\ + \x01\x20\x01(\tR\x04data\"9\n\x0bURLCellData\x12\x10\n\x03url\x18\x01\ + \x20\x01(\tR\x03url\x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07contentb\ + \x06proto3\ +"; + +static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; + +fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() +} + +pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) +} diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/text_type_option.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/text_type_option.proto index 67cfb438ea..827c569a74 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/text_type_option.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/text_type_option.proto @@ -1,5 +1,5 @@ syntax = "proto3"; message RichTextTypeOption { - string format = 1; + string data = 1; } diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/url_type_option.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/url_type_option.proto new file mode 100644 index 0000000000..edf1c7e341 --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/url_type_option.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +message URLTypeOption { + string data = 1; +} +message URLCellData { + string url = 1; + string content = 2; +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs b/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs index 5eaabb0294..7978323be1 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/field_builder.rs @@ -94,6 +94,7 @@ pub fn default_type_option_builder_from_type(field_type: &FieldType) -> Box SingleSelectTypeOption::default().into(), FieldType::MultiSelect => MultiSelectTypeOption::default().into(), FieldType::Checkbox => CheckboxTypeOption::default().into(), + FieldType::URL => URLTypeOption::default().into(), }; type_option_builder_from_json_str(&s, field_type) @@ -107,6 +108,7 @@ pub fn type_option_builder_from_json_str(s: &str, field_type: &FieldType) -> Box FieldType::SingleSelect => Box::new(SingleSelectTypeOptionBuilder::from_json_str(s)), FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_json_str(s)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_json_str(s)), + FieldType::URL => Box::new(URLTypeOptionBuilder::from_json_str(s)), } } @@ -119,5 +121,6 @@ pub fn type_option_builder_from_bytes>(bytes: T, field_type: &Fie FieldType::SingleSelect => Box::new(SingleSelectTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_protobuf_bytes(bytes)), + FieldType::URL => Box::new(URLTypeOptionBuilder::from_protobuf_bytes(bytes)), } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs index 2c74b2097b..3cfe390b38 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/mod.rs @@ -3,6 +3,7 @@ mod date_type_option; mod number_type_option; mod selection_type_option; mod text_type_option; +mod url_type_option; mod util; pub use checkbox_type_option::*; @@ -10,3 +11,4 @@ pub use date_type_option::*; pub use number_type_option::*; pub use selection_type_option::*; pub use text_type_option::*; +pub use url_type_option::*; 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 d500bbcc39..db6f7dffde 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 @@ -736,14 +736,10 @@ mod tests { ) { assert_eq!( type_option - .decode_cell_data(data(cell_data), field_type, field_meta) + .decode_cell_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/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs index 32d45a3e29..199ddf38de 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 @@ -27,7 +27,7 @@ impl TypeOptionBuilder for RichTextTypeOptionBuilder { #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)] pub struct RichTextTypeOption { #[pb(index = 1)] - pub format: String, + data: String, //It's not used. } impl_type_option!(RichTextTypeOption, FieldType::RichText); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs new file mode 100644 index 0000000000..5ca29ee87d --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -0,0 +1,101 @@ +use crate::impl_type_option; +use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; +use bytes::Bytes; +use flowy_derive::ProtoBuf; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_grid_data_model::entities::{ + CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, +}; + +use serde::{Deserialize, Serialize}; + +#[derive(Default)] +pub struct URLTypeOptionBuilder(URLTypeOption); +impl_into_box_type_option_builder!(URLTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(URLTypeOptionBuilder, URLTypeOption); + +impl TypeOptionBuilder for URLTypeOptionBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] +pub struct URLTypeOption { + #[pb(index = 1)] + data: String, //It's not used. +} +impl_type_option!(URLTypeOption, FieldType::URL); + +impl CellDataOperation for URLTypeOption { + fn decode_cell_data( + &self, + encoded_data: T, + decoded_field_type: &FieldType, + _field_meta: &FieldMeta, + ) -> FlowyResult + where + T: Into, + { + if !decoded_field_type.is_url() { + return Ok(DecodedCellData::default()); + } + + let cell_data = encoded_data.into(); + Ok(DecodedCellData::from_content(cell_data)) + } + + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { + let changeset = changeset.into(); + Ok(changeset.to_string()) + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] +pub struct URLCellData { + #[pb(index = 1)] + pub url: String, + + #[pb(index = 2)] + pub content: String, +} + +#[cfg(test)] +mod tests { + use crate::services::field::FieldBuilder; + use crate::services::field::URLTypeOption; + use crate::services::row::CellDataOperation; + use flowy_grid_data_model::entities::{FieldMeta, FieldType}; + + #[test] + fn url_type_option_format_test() { + let type_option = URLTypeOption::default(); + let field_type = FieldType::URL; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + assert_equal(&type_option, "123", "123", &field_type, &field_meta); + } + + fn assert_equal( + type_option: &URLTypeOption, + cell_data: &str, + expected_str: &str, + field_type: &FieldType, + field_meta: &FieldMeta, + ) { + assert_eq!( + type_option + .decode_cell_data(cell_data, field_type, field_meta) + .unwrap() + .content, + expected_str.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 9911e4cab3..28fc0527a1 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 @@ -133,6 +133,7 @@ pub fn apply_cell_data_changeset>( 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), + FieldType::URL => URLTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), }?; Ok(TypeOptionCellData::new(s, field_meta.field_type.clone()).json()) @@ -178,6 +179,9 @@ pub fn decode_cell_data>( FieldType::Checkbox => field_meta .get_type_option_entry::(t_field_type)? .decode_cell_data(encoded_data, s_field_type, field_meta), + FieldType::URL => field_meta + .get_type_option_entry::(t_field_type)? + .decode_cell_data(encoded_data, s_field_type, field_meta), }; Some(data) }; 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 e8f3d615c4..e51f2222e2 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -880,6 +880,7 @@ pub enum FieldType { SingleSelect = 3, MultiSelect = 4, Checkbox = 5, + URL = 6, } impl std::default::Default for FieldType { @@ -937,6 +938,10 @@ impl FieldType { self == &FieldType::MultiSelect } + pub fn is_url(&self) -> bool { + self == &FieldType::URL + } + pub fn is_select_option(&self) -> bool { self == &FieldType::MultiSelect || self == &FieldType::SingleSelect } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 0cda103da8..8c94e1aa30 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -8231,6 +8231,7 @@ pub enum FieldType { SingleSelect = 3, MultiSelect = 4, Checkbox = 5, + URL = 6, } impl ::protobuf::ProtobufEnum for FieldType { @@ -8246,6 +8247,7 @@ impl ::protobuf::ProtobufEnum for FieldType { 3 => ::std::option::Option::Some(FieldType::SingleSelect), 4 => ::std::option::Option::Some(FieldType::MultiSelect), 5 => ::std::option::Option::Some(FieldType::Checkbox), + 6 => ::std::option::Option::Some(FieldType::URL), _ => ::std::option::Option::None } } @@ -8258,6 +8260,7 @@ impl ::protobuf::ProtobufEnum for FieldType { FieldType::SingleSelect, FieldType::MultiSelect, FieldType::Checkbox, + FieldType::URL, ]; values } @@ -8380,10 +8383,10 @@ static file_descriptor_proto_data: &'static [u8] = b"\ ld_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content_changeset\ \x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_of_cell_co\ ntent_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\x12\x0b\n\ - \x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\ + \x07MoveRow\x10\x01*m\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\ \x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSele\ ct\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\ - \x05b\x06proto3\ + \x05\x12\x07\n\x03URL\x10\x06b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index a2d74e96a3..b19ba4ef82 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -168,4 +168,5 @@ enum FieldType { SingleSelect = 3; MultiSelect = 4; Checkbox = 5; + URL = 6; } From 40e7b42a63235abdaae9dac0b2d01a35d876c125 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 27 May 2022 19:57:13 +0800 Subject: [PATCH 25/82] chore: fix flutter warnings --- frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart index 35457ff989..ca3596e48a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart @@ -9,7 +9,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-net/event.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user/event_map.pb.dart'; From 7a7ec1008563839cd153297c1556252353417630 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 27 May 2022 20:26:33 +0800 Subject: [PATCH 26/82] chore: add test comand in cargo make --- frontend/Makefile.toml | 1 + frontend/scripts/makefile/tests.toml | 36 ++++++++++------------------ frontend/scripts/makefile/tool.toml | 27 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 frontend/scripts/makefile/tool.toml diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 7c82e17ba7..c46687a2df 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -7,6 +7,7 @@ extend = [ { path = "scripts/makefile/docker.toml" }, { path = "scripts/makefile/env.toml" }, { path = "scripts/makefile/flutter.toml" }, + { path = "scripts/makefile/tool.toml" }, ] [config] diff --git a/frontend/scripts/makefile/tests.toml b/frontend/scripts/makefile/tests.toml index 89e3ea9140..b8082f651b 100644 --- a/frontend/scripts/makefile/tests.toml +++ b/frontend/scripts/makefile/tests.toml @@ -1,30 +1,18 @@ -[tasks.test_local] -category = "Build" -dependencies = ["rm_cache"] -description = "Build desktop targets." +[tasks.rust_unit_test] +run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] } + +[tasks.rust_lib_unit_test] +description = "Run rust-lib unit tests" script = ''' cd rust-lib -cargo test +RUST_LOG=info cargo test --no-default-features --features="sync" ''' +[tasks.shared_lib_unit_test] +description = "Run shared-lib unit test" +script = ''' +cd ../shared-lib +RUST_LOG=info cargo test --no-default-features +''' -[tasks.test_remote] -dependencies = ["rm_cache"] -script = """ -cd rust-lib -cargo test --features "flowy-folder/http_server","flowy-user/http_server" -""" - - -[tasks.run_server] -script = """ -cd backend -cargo run -""" - - -[tasks.rm_cache] -script = """ -rm -rf rust-lib/flowy-test/temp -""" \ No newline at end of file diff --git a/frontend/scripts/makefile/tool.toml b/frontend/scripts/makefile/tool.toml new file mode 100644 index 0000000000..bd0504f6fc --- /dev/null +++ b/frontend/scripts/makefile/tool.toml @@ -0,0 +1,27 @@ +[tasks.rust_clean] +script = [ + """ + cd rust-lib + cargo clean + + cd ../../shared-lib + cargo clean + + rm -rf lib-infra/.cache + """, +] +script_runner = "@shell" + +[tasks.rust_clean.windows] +script = [ + """ + cd rust-lib + cargo clean + + cd ../../shared-lib + cargo clean + + rmdir /s/q "lib-infra/.cache" + """, +] +script_runner = "@duckscript" \ No newline at end of file From c4db17f73c3634a47d3c9d56744484f361e724eb Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Fri, 27 May 2022 10:34:12 +0800 Subject: [PATCH 27/82] feat: frameless window for mac --- .../app_flowy/lib/core/frameless_window.dart | 67 ++++++++++++++++++ .../workspace/application/home/home_bloc.dart | 6 ++ .../workspace/application/menu/menu_bloc.dart | 7 -- .../presentation/home/home_stack.dart | 15 +++- .../presentation/home/menu/menu.dart | 34 ++++++--- .../presentation/home/navigation.dart | 2 + .../macos/Runner/MainFlutterWindow.swift | 70 +++++++++++++++++++ 7 files changed, 183 insertions(+), 18 deletions(-) create mode 100644 frontend/app_flowy/lib/core/frameless_window.dart diff --git a/frontend/app_flowy/lib/core/frameless_window.dart b/frontend/app_flowy/lib/core/frameless_window.dart new file mode 100644 index 0000000000..a7d6417cd3 --- /dev/null +++ b/frontend/app_flowy/lib/core/frameless_window.dart @@ -0,0 +1,67 @@ +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import 'dart:io' show Platform; + +class CocoaWindowChannel { + CocoaWindowChannel._(); + + final MethodChannel _channel = const MethodChannel("flutter/cocoaWindow"); + + static final CocoaWindowChannel instance = CocoaWindowChannel._(); + + Future setWindowPosition(Offset offset) async { + await _channel.invokeMethod("setWindowPosition", [offset.dx, offset.dy]); + } + + Future> getWindowPosition() async { + final raw = await _channel.invokeMethod("getWindowPosition"); + final arr = raw as List; + final List result = arr.map((s) => s as double).toList(); + return result; + } + + Future zoom() async { + await _channel.invokeMethod("zoom"); + } +} + +class MoveWindowDetector extends StatefulWidget { + const MoveWindowDetector({Key? key, this.child}) : super(key: key); + + final Widget? child; + + @override + _MoveWindowDetectorState createState() => _MoveWindowDetectorState(); +} + +class _MoveWindowDetectorState extends State { + double winX = 0; + double winY = 0; + + @override + Widget build(BuildContext context) { + if (!Platform.isMacOS) { + return widget.child ?? Container(); + } + return GestureDetector( + // https://stackoverflow.com/questions/52965799/flutter-gesturedetector-not-working-with-containers-in-stack + behavior: HitTestBehavior.translucent, + onDoubleTap: () async { + await CocoaWindowChannel.instance.zoom(); + }, + onPanStart: (DragStartDetails details) { + winX = details.globalPosition.dx; + winY = details.globalPosition.dy; + }, + onPanUpdate: (DragUpdateDetails details) async { + final windowPos = await CocoaWindowChannel.instance.getWindowPosition(); + final double dx = windowPos[0]; + final double dy = windowPos[1]; + final deltaX = details.globalPosition.dx - winX; + final deltaY = details.globalPosition.dy - winY; + await CocoaWindowChannel.instance.setWindowPosition(Offset(dx + deltaX, dy - deltaY)); + }, + child: widget.child, + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart b/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart index 796a0357b9..f3d9930842 100644 --- a/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart @@ -49,6 +49,9 @@ class HomeBloc extends Bloc { unauthorized: (_Unauthorized value) { emit(state.copyWith(unauthorized: true)); }, + collapseMenu: (e) { + emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed)); + }, ); }); } @@ -77,6 +80,7 @@ class HomeEvent with _$HomeEvent { const factory HomeEvent.dismissEditPannel() = _DismissEditPannel; const factory HomeEvent.didReceiveWorkspaceSetting(CurrentWorkspaceSetting setting) = _DidReceiveWorkspaceSetting; const factory HomeEvent.unauthorized(String msg) = _Unauthorized; + const factory HomeEvent.collapseMenu() = _CollapseMenu; } @freezed @@ -87,6 +91,7 @@ class HomeState with _$HomeState { required Option pannelContext, required CurrentWorkspaceSetting workspaceSetting, required bool unauthorized, + required bool isMenuCollapsed, }) = _HomeState; factory HomeState.initial(CurrentWorkspaceSetting workspaceSetting) => HomeState( @@ -95,5 +100,6 @@ class HomeState with _$HomeState { pannelContext: none(), workspaceSetting: workspaceSetting, unauthorized: false, + isMenuCollapsed: false, ); } diff --git a/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart b/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart index a2c167cde4..db8f2c534b 100644 --- a/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart @@ -25,10 +25,6 @@ class MenuBloc extends Bloc { listener.start(addAppCallback: _handleAppsOrFail); await _fetchApps(emit); }, - collapse: (e) async { - final isCollapse = state.isCollapse; - emit(state.copyWith(isCollapse: !isCollapse)); - }, openPage: (e) async { emit(state.copyWith(plugin: e.plugin)); }, @@ -94,7 +90,6 @@ class MenuBloc extends Bloc { @freezed class MenuEvent with _$MenuEvent { const factory MenuEvent.initial() = _Initial; - const factory MenuEvent.collapse() = _Collapse; const factory MenuEvent.openPage(Plugin plugin) = _OpenPage; const factory MenuEvent.createApp(String name, {String? desc}) = _CreateApp; const factory MenuEvent.moveApp(int fromIndex, int toIndex) = _MoveApp; @@ -104,14 +99,12 @@ class MenuEvent with _$MenuEvent { @freezed class MenuState with _$MenuState { const factory MenuState({ - required bool isCollapse, required List apps, required Either successOrFailure, required Plugin plugin, }) = _MenuState; factory MenuState.initial() => MenuState( - isCollapse: false, apps: [], successOrFailure: left(unit), plugin: makePlugin(pluginType: DefaultPlugin.blank.type()), diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart index 65e315f56d..c16c965a82 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart @@ -1,8 +1,12 @@ +import 'dart:io' show Platform; + import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/workspace/application/home/home_bloc.dart'; import 'package:app_flowy/workspace/presentation/home/home_screen.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:time/time.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -11,6 +15,7 @@ import 'package:app_flowy/plugin/plugin.dart'; import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart'; import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; import 'package:app_flowy/workspace/presentation/home/navigation.dart'; +import 'package:app_flowy/core/frameless_window.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/style_widget/extension.dart'; import 'package:flowy_infra/notifier.dart'; @@ -152,7 +157,7 @@ class HomeStackManager { child: Selector( selector: (context, notifier) => notifier.titleWidget, builder: (context, widget, child) { - return const HomeTopBar(); + return const MoveWindowDetector(child: HomeTopBar()); }, ), ); @@ -191,6 +196,14 @@ class HomeTopBar extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ + BlocBuilder( + buildWhen: ((previous, current) => previous.isMenuCollapsed != current.isMenuCollapsed), + builder: (context, state) { + if (state.isMenuCollapsed && Platform.isMacOS) { + return const HSpace(80); + } + return const HSpace(0); + }), const FlowyNavigation(), const HSpace(16), ChangeNotifierProvider.value( diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart index b888ab7631..0eb22e3d1f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart @@ -1,6 +1,8 @@ export './app/header/header.dart'; export './app/menu_app.dart'; +import 'dart:io' show Platform; +import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/plugins/trash/menu.dart'; import 'package:flowy_infra/notifier.dart'; @@ -18,7 +20,9 @@ import 'package:expandable/expandable.dart'; import 'package:flowy_infra/time/duration.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/menu/menu_bloc.dart'; -import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; +import 'package:app_flowy/workspace/application/home/home_bloc.dart'; +import 'package:app_flowy/core/frameless_window.dart'; +// import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; @@ -59,10 +63,10 @@ class HomeMenu extends StatelessWidget { getIt().setPlugin(state.plugin); }, ), - BlocListener( - listenWhen: (p, c) => p.isCollapse != c.isCollapse, + BlocListener( + listenWhen: (p, c) => p.isMenuCollapsed != c.isMenuCollapsed, listener: (context, state) { - _collapsedNotifier.value = state.isCollapse; + _collapsedNotifier.value = state.isMenuCollapsed; }, ) ], @@ -179,6 +183,17 @@ class MenuSharedState { class MenuTopBar extends StatelessWidget { const MenuTopBar({Key? key}) : super(key: key); + + Widget renderIcon(BuildContext context) { + if (Platform.isMacOS) { + return Container(); + } + final theme = context.watch(); + return (theme.isDark + ? svgWithSize("flowy_logo_dark_mode", const Size(92, 17)) + : svgWithSize("flowy_logo_with_text", const Size(92, 17))); + } + @override Widget build(BuildContext context) { final theme = context.watch(); @@ -186,20 +201,19 @@ class MenuTopBar extends StatelessWidget { builder: (context, state) { return SizedBox( height: HomeSizes.topBarHeight, - child: Row( + child: MoveWindowDetector( + child: Row( children: [ - (theme.isDark - ? svgWithSize("flowy_logo_dark_mode", const Size(92, 17)) - : svgWithSize("flowy_logo_with_text", const Size(92, 17))), + renderIcon(context), const Spacer(), FlowyIconButton( width: 28, - onPressed: () => context.read().add(const MenuEvent.collapse()), + onPressed: () => context.read().add(const HomeEvent.collapseMenu()), iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), icon: svgWidget("home/hide_menu", color: theme.iconColor), ) ], - ), + )), ); }, ); diff --git a/frontend/app_flowy/lib/workspace/presentation/home/navigation.dart b/frontend/app_flowy/lib/workspace/presentation/home/navigation.dart index b6947c79fe..f52b7224f6 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/navigation.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/navigation.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/application/home/home_bloc.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/notifier.dart'; @@ -95,6 +96,7 @@ class FlowyNavigation extends StatelessWidget { width: 24, onPressed: () { notifier.value = false; + ctx.read().add(const HomeEvent.collapseMenu()); }, iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2), icon: svgWidget("home/hide_menu", color: theme.iconColor), diff --git a/frontend/app_flowy/macos/Runner/MainFlutterWindow.swift b/frontend/app_flowy/macos/Runner/MainFlutterWindow.swift index 2722837ec9..8e357d7ca1 100644 --- a/frontend/app_flowy/macos/Runner/MainFlutterWindow.swift +++ b/frontend/app_flowy/macos/Runner/MainFlutterWindow.swift @@ -1,12 +1,82 @@ import Cocoa import FlutterMacOS +private let kTrafficLightOffetTop = 22 + class MainFlutterWindow: NSWindow { + func registerMethodChannel(flutterViewController: FlutterViewController) { + let cocoaWindowChannel = FlutterMethodChannel(name: "flutter/cocoaWindow", binaryMessenger: flutterViewController.engine.binaryMessenger) + cocoaWindowChannel.setMethodCallHandler({ + (call: FlutterMethodCall, result: FlutterResult) -> Void in + if call.method == "setWindowPosition" { + guard let position = call.arguments as? NSArray else { + result(nil) + return + } + let nX = position[0] as! NSNumber + let nY = position[1] as! NSNumber + let x = nX.doubleValue + let y = nY.doubleValue + + self.setFrameOrigin(NSPoint(x: x, y: y)) + result(nil) + return + } else if call.method == "getWindowPosition" { + let frame = self.frame + result([frame.origin.x, frame.origin.y]) + return + } else if call.method == "zoom" { + self.zoom(self) + result(nil) + return + } + + result(FlutterMethodNotImplemented) + }) + } + + func layoutTrafficLightButton(titlebarView: NSView, button: NSButton, offsetTop: CGFloat, offsetLeft: CGFloat) { + button.translatesAutoresizingMaskIntoConstraints = false; + titlebarView.addConstraint(NSLayoutConstraint.init( + item: button, + attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: titlebarView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1, constant: offsetTop)) + titlebarView.addConstraint(NSLayoutConstraint.init( + item: button, + attribute: NSLayoutConstraint.Attribute.left, relatedBy: NSLayoutConstraint.Relation.equal, toItem: titlebarView, attribute: NSLayoutConstraint.Attribute.left, multiplier: 1, constant: offsetLeft)) + } + + func layoutTrafficLights() { + let closeButton = self.standardWindowButton(ButtonType.closeButton)! + let minButton = self.standardWindowButton(ButtonType.miniaturizeButton)! + let zoomButton = self.standardWindowButton(ButtonType.zoomButton)! + let titlebarView = closeButton.superview! + + self.layoutTrafficLightButton(titlebarView: titlebarView, button: closeButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 20) + self.layoutTrafficLightButton(titlebarView: titlebarView, button: minButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 38) + self.layoutTrafficLightButton(titlebarView: titlebarView, button: zoomButton, offsetTop: CGFloat(kTrafficLightOffetTop), offsetLeft: 56) + + let customToolbar = NSTitlebarAccessoryViewController() + let newView = NSView() + newView.frame = NSRect(origin: CGPoint(), size: CGSize(width: 0, height: 40)) // only the height is cared + customToolbar.view = newView + self.addTitlebarAccessoryViewController(customToolbar) + } + override func awakeFromNib() { let flutterViewController = FlutterViewController.init() let windowFrame = self.frame self.contentViewController = flutterViewController + + self.registerMethodChannel(flutterViewController: flutterViewController) + self.setFrame(windowFrame, display: true) + self.titlebarAppearsTransparent = true + self.titleVisibility = .hidden + self.styleMask.insert(StyleMask.fullSizeContentView) + self.isMovableByWindowBackground = true + self.isMovable = false + + self.layoutTrafficLights() RegisterGeneratedPlugins(registry: flutterViewController) From 166438ad73f9de9a8c9bb458cfdbb713e5dc855e Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 27 May 2022 20:19:22 +0800 Subject: [PATCH 28/82] chore: fix rust unit test --- frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs | 5 +++++ frontend/rust-lib/flowy-grid/tests/grid/script.rs | 5 +++++ 2 files changed, 10 insertions(+) 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 f4549cb726..b78f9be551 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs @@ -262,6 +262,9 @@ async fn grid_row_add_cells_test() { FieldType::Checkbox => { builder.add_cell(&field.id, "false".to_string()).unwrap(); } + FieldType::URL => { + builder.add_cell(&field.id, "1".to_string()).unwrap(); + } } } let context = builder.build(); @@ -327,6 +330,7 @@ async fn grid_cell_update() { SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str() } FieldType::Checkbox => "1".to_string(), + FieldType::URL => "1".to_string(), }; scripts.push(UpdateCell { @@ -348,6 +352,7 @@ async fn grid_cell_update() { FieldType::SingleSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false), FieldType::MultiSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false), FieldType::Checkbox => ("2".to_string(), false), + FieldType::URL => ("2".to_string(), false), }; scripts.push(UpdateCell { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/script.rs index 611ee937e2..956bd4dd6c 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/script.rs @@ -354,6 +354,10 @@ fn make_template_1_grid() -> BuildGridContext { let checkbox = CheckboxTypeOptionBuilder::default(); let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build(); + // URL + let url = URLTypeOptionBuilder::default(); + let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); + GridBuilder::default() .add_field(text_field) .add_field(single_select_field) @@ -361,6 +365,7 @@ fn make_template_1_grid() -> BuildGridContext { .add_field(number_field) .add_field(date_field) .add_field(checkbox_field) + .add_field(url_field) .add_empty_row() .add_empty_row() .add_empty_row() From 8d766f3bb43cc9993c53fdff21dc6ac1032c7494 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 28 May 2022 08:35:22 +0800 Subject: [PATCH 29/82] refactor: cell data loader --- .../grid/cell/cell_service/data_loader.dart | 12 +- .../grid/cell/checkbox_cell_bloc.dart | 22 ++-- .../grid/cell/number_cell_bloc.dart | 13 +-- .../application/grid/cell/text_cell_bloc.dart | 13 +-- .../flowy-grid-data-model/grid.pb.dart | 8 +- .../flowy-grid-data-model/grid.pbjson.dart | 4 +- .../src/services/row/cell_data_operation.rs | 13 ++- .../src/entities/grid.rs | 6 +- .../src/protobuf/model/grid.rs | 106 +++++++++--------- .../src/protobuf/proto/grid.proto | 2 +- 10 files changed, 99 insertions(+), 100 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart index 5be0435323..2e1801103e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart @@ -30,7 +30,11 @@ abstract class IGridCellDataLoader { IGridCellDataConfig get config; } -class GridCellDataLoader extends IGridCellDataLoader { +abstract class ICellDataParser { + T? parserData(); +} + +class GridCellDataLoader extends IGridCellDataLoader { final CellService service = CellService(); final GridCell gridCell; @@ -43,16 +47,16 @@ class GridCellDataLoader extends IGridCellDataLoader { }); @override - Future loadData() { + Future loadData() { final fut = service.getCell( gridId: gridCell.gridId, fieldId: gridCell.field.id, rowId: gridCell.rowId, ); return fut.then((result) { - return result.fold((data) => data, (err) { + return result.fold((Cell data) => data.content, (err) { Log.error(err); - return null; + return ""; }); }); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart index cecfaa2f04..514ae2ce4e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart @@ -1,4 +1,3 @@ -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -16,15 +15,15 @@ class CheckboxCellBloc extends Bloc { }) : super(CheckboxCellState.initial(cellContext)) { on( (event, emit) async { - await event.map( - initial: (_Initial value) { + await event.when( + initial: () { _startListening(); }, - select: (_Selected value) async { + select: () async { _updateCellData(); }, - didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith(isSelected: _isSelected(value.cell))); + didReceiveCellUpdate: (cellData) { + emit(state.copyWith(isSelected: _isSelected(cellData))); }, ); }, @@ -43,9 +42,9 @@ class CheckboxCellBloc extends Bloc { } void _startListening() { - _onCellChangedFn = cellContext.startListening(onCellChanged: ((cell) { + _onCellChangedFn = cellContext.startListening(onCellChanged: ((cellData) { if (!isClosed) { - add(CheckboxCellEvent.didReceiveCellUpdate(cell)); + add(CheckboxCellEvent.didReceiveCellUpdate(cellData)); } })); } @@ -59,7 +58,7 @@ class CheckboxCellBloc extends Bloc { class CheckboxCellEvent with _$CheckboxCellEvent { const factory CheckboxCellEvent.initial() = _Initial; const factory CheckboxCellEvent.select() = _Selected; - const factory CheckboxCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; + const factory CheckboxCellEvent.didReceiveCellUpdate(String cellData) = _DidReceiveCellUpdate; } @freezed @@ -73,7 +72,6 @@ class CheckboxCellState with _$CheckboxCellState { } } -bool _isSelected(Cell? cell) { - final content = cell?.content ?? ""; - return content == "Yes"; +bool _isSelected(String? cellData) { + return cellData == "Yes"; } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart index 217ae4d384..ceb89bc201 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart @@ -1,4 +1,3 @@ -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -20,7 +19,7 @@ class NumberCellBloc extends Bloc { _startListening(); }, didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith(content: value.cell.content)); + emit(state.copyWith(content: value.cellContent)); }, updateCell: (_UpdateCell value) async { await _updateCellValue(value, emit); @@ -46,9 +45,9 @@ class NumberCellBloc extends Bloc { void _startListening() { _onCellChangedFn = cellContext.startListening( - onCellChanged: ((cell) { + onCellChanged: ((cellContent) { if (!isClosed) { - add(NumberCellEvent.didReceiveCellUpdate(cell)); + add(NumberCellEvent.didReceiveCellUpdate(cellContent)); } }), ); @@ -59,7 +58,7 @@ class NumberCellBloc extends Bloc { class NumberCellEvent with _$NumberCellEvent { const factory NumberCellEvent.initial() = _Initial; const factory NumberCellEvent.updateCell(String text) = _UpdateCell; - const factory NumberCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; + const factory NumberCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate; } @freezed @@ -69,7 +68,7 @@ class NumberCellState with _$NumberCellState { }) = _NumberCellState; factory NumberCellState.initial(GridCellContext context) { - final cell = context.getCellData(); - return NumberCellState(content: cell?.content ?? ""); + final cellContent = context.getCellData() ?? ""; + return NumberCellState(content: cellContent); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart index 0b8b8be5e0..2123802966 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart @@ -1,4 +1,3 @@ -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -26,9 +25,7 @@ class TextCellBloc extends Bloc { emit(state.copyWith(content: value.cellData.cell?.content ?? "")); }, didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith( - content: value.cell.content, - )); + emit(state.copyWith(content: value.cellContent)); }, ); }, @@ -47,9 +44,9 @@ class TextCellBloc extends Bloc { void _startListening() { _onCellChangedFn = cellContext.startListening( - onCellChanged: ((cell) { + onCellChanged: ((cellContent) { if (!isClosed) { - add(TextCellEvent.didReceiveCellUpdate(cell)); + add(TextCellEvent.didReceiveCellUpdate(cellContent)); } }), ); @@ -60,7 +57,7 @@ class TextCellBloc extends Bloc { class TextCellEvent with _$TextCellEvent { const factory TextCellEvent.initial() = _InitialCell; const factory TextCellEvent.didReceiveCellData(GridCell cellData) = _DidReceiveCellData; - const factory TextCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; + const factory TextCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate; const factory TextCellEvent.updateText(String text) = _UpdateText; } @@ -71,6 +68,6 @@ class TextCellState with _$TextCellState { }) = _TextCellState; factory TextCellState.initial(GridCellContext context) => TextCellState( - content: context.getCellData()?.content ?? "", + content: context.getCellData() ?? "", ); } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index a1ecd8c059..19a1aca54d 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -1367,7 +1367,7 @@ class Cell extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Cell', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data') + ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) ..hasRequiredFields = false ; @@ -1375,7 +1375,7 @@ class Cell extends $pb.GeneratedMessage { factory Cell({ $core.String? fieldId, $core.String? content, - $core.String? data, + $core.List<$core.int>? data, }) { final _result = create(); if (fieldId != null) { @@ -1429,9 +1429,9 @@ class Cell extends $pb.GeneratedMessage { void clearContent() => clearField(2); @$pb.TagNumber(3) - $core.String get data => $_getSZ(2); + $core.List<$core.int> get data => $_getN(2); @$pb.TagNumber(3) - set data($core.String v) { $_setString(2, v); } + set data($core.List<$core.int> v) { $_setBytes(2, v); } @$pb.TagNumber(3) $core.bool hasData() => $_has(2); @$pb.TagNumber(3) diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index 3a925cb282..c203258374 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -290,12 +290,12 @@ const Cell$json = const { '2': const [ const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'}, const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, - const {'1': 'data', '3': 3, '4': 1, '5': 9, '10': 'data'}, + const {'1': 'data', '3': 3, '4': 1, '5': 12, '10': 'data'}, ], }; /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoCVIEZGF0YQ=='); +final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoDFIEZGF0YQ=='); @$core.Deprecated('Use repeatedCellDescriptor instead') const RepeatedCell$json = const { '1': 'RepeatedCell', 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 9911e4cab3..f92e4ac8d2 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 @@ -226,23 +226,24 @@ where #[derive(Default)] pub struct DecodedCellData { - raw: String, + pub data: Vec, pub content: String, } impl DecodedCellData { pub fn from_content(content: String) -> Self { Self { - raw: content.clone(), + data: content.as_bytes().to_vec(), content, } } - pub fn new(raw: String, content: String) -> Self { - Self { raw, content } + pub fn new>(data: T, content: String) -> Self { + let data = data.as_ref().to_vec(); + Self { data, content } } - pub fn split(self) -> (String, String) { - (self.raw, self.content) + pub fn split(self) -> (Vec, String) { + (self.data, self.content) } } 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 e8f3d615c4..dab20b772c 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -487,11 +487,11 @@ pub struct Cell { pub content: String, #[pb(index = 3)] - pub data: String, + pub data: Vec, } impl Cell { - pub fn new(field_id: &str, content: String, data: String) -> Self { + pub fn new(field_id: &str, content: String, data: Vec) -> Self { Self { field_id: field_id.to_owned(), content, @@ -503,7 +503,7 @@ impl Cell { Self { field_id: field_id.to_owned(), content: "".to_string(), - data: "".to_string(), + data: vec![], } } } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 0cda103da8..e5f9102fd5 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -4744,7 +4744,7 @@ pub struct Cell { // message fields pub field_id: ::std::string::String, pub content: ::std::string::String, - pub data: ::std::string::String, + pub data: ::std::vec::Vec, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -4813,10 +4813,10 @@ impl Cell { ::std::mem::replace(&mut self.content, ::std::string::String::new()) } - // string data = 3; + // bytes data = 3; - pub fn get_data(&self) -> &str { + pub fn get_data(&self) -> &[u8] { &self.data } pub fn clear_data(&mut self) { @@ -4824,19 +4824,19 @@ impl Cell { } // Param is passed by value, moved - pub fn set_data(&mut self, v: ::std::string::String) { + pub fn set_data(&mut self, v: ::std::vec::Vec) { self.data = v; } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_data(&mut self) -> &mut ::std::string::String { + pub fn mut_data(&mut self) -> &mut ::std::vec::Vec { &mut self.data } // Take field - pub fn take_data(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.data, ::std::string::String::new()) + pub fn take_data(&mut self) -> ::std::vec::Vec { + ::std::mem::replace(&mut self.data, ::std::vec::Vec::new()) } } @@ -4856,7 +4856,7 @@ impl ::protobuf::Message for Cell { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; }, 3 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.data)?; + ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; @@ -4877,7 +4877,7 @@ impl ::protobuf::Message for Cell { my_size += ::protobuf::rt::string_size(2, &self.content); } if !self.data.is_empty() { - my_size += ::protobuf::rt::string_size(3, &self.data); + my_size += ::protobuf::rt::bytes_size(3, &self.data); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -4892,7 +4892,7 @@ impl ::protobuf::Message for Cell { os.write_string(2, &self.content)?; } if !self.data.is_empty() { - os.write_string(3, &self.data)?; + os.write_bytes(3, &self.data)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -4942,7 +4942,7 @@ impl ::protobuf::Message for Cell { |m: &Cell| { &m.content }, |m: &mut Cell| { &mut m.content }, )); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( "data", |m: &Cell| { &m.data }, |m: &mut Cell| { &mut m.data }, @@ -8342,48 +8342,48 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ Orders\"O\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\ \x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\x12\x12\n\x04data\ - \x18\x03\x20\x01(\tR\x04data\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\ - \x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\ - \n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05va\ - lue\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\ - \x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid\ - _id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\ - \x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12Ins\ - ertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\ - \x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type\ - _option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_fie\ - ld_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_fiel\ - d_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\x07grid_id\x18\x01\ - \x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fiel\ - dId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"\ - d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gri\ - dId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\ - \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\ - \x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetPayload\ - \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\ - id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\x01(\tH\0\ - R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfie\ - ld_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\x12\x18\n\x06\ - frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvisibility\x18\ - \x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\x08\x20\x01(\ - \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\ - \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\ - _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\ - \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\ - emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\ - \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\ - \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\ - \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\ - \"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06\ - gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08fie\ - ld_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content_changeset\ - \x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_of_cell_co\ - ntent_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\x12\x0b\n\ - \x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\ - \x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSele\ - ct\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\ - \x05b\x06proto3\ + \x18\x03\x20\x01(\x0cR\x04data\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\ + \x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\ + \x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\ + \x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05\ + value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\ + \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\ + \x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\ + \n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gri\ + dId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\ + \x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0es\ + tart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_st\ + art_field_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\x07grid_id\ + \x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\ + \x07fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOpti\ + onData\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\t\ + R\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFie\ + ldOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid\ + _id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03\ + (\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetP\ + ayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\ + \x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\ + \x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\ + \x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\ + \x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvis\ + ibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\ + \x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\ + \x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_des\ + cB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_vis\ + ibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\ + \x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\ + idId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_\ + index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\ + \x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTy\ + peR\x02ty\"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\ + \x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\ + \x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content\ + _changeset\x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_\ + of_cell_content_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\ + \x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\ + \x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSi\ + ngleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbo\ + x\x10\x05b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index a2d74e96a3..d762e65e70 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -96,7 +96,7 @@ message GridBlock { message Cell { string field_id = 1; string content = 2; - string data = 3; + bytes data = 3; } message RepeatedCell { repeated Cell items = 1; From 62c322ab219479be2f065f8171da9aeb749ead94 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 28 May 2022 15:30:15 +0800 Subject: [PATCH 30/82] refactor: Date & SingleSelect & Multi-Select type option --- .../grid/cell/cell_service/cell_service.dart | 2 +- .../cell/cell_service/context_builder.dart | 40 ++-- .../grid/cell/cell_service/data_loader.dart | 86 ++++---- .../grid/cell/select_option_cell_bloc.dart | 5 - .../grid/cell/select_option_editor_bloc.dart | 21 ++ .../application/grid/cell/text_cell_bloc.dart | 18 +- .../dart_event/flowy-grid/dart_event.dart | 17 -- .../flowy-grid-data-model/grid.pb.dart | 24 +-- .../flowy-grid-data-model/grid.pbjson.dart | 5 +- .../protobuf/flowy-grid/event_map.pbenum.dart | 2 - .../protobuf/flowy-grid/event_map.pbjson.dart | 3 +- .../rust-lib/flowy-grid/src/event_handler.rs | 21 -- frontend/rust-lib/flowy-grid/src/event_map.rs | 4 - .../src/protobuf/model/event_map.rs | 7 +- .../src/protobuf/proto/event_map.proto | 1 - .../type_options/checkbox_type_option.rs | 14 +- .../field/type_options/date_type_option.rs | 183 ++++++------------ .../field/type_options/number_type_option.rs | 10 +- .../type_options/selection_type_option.rs | 153 ++++++++------- .../field/type_options/text_type_option.rs | 26 ++- .../src/services/row/cell_data_operation.rs | 55 ++++-- .../flowy-grid/src/services/row/row_loader.rs | 10 +- .../flowy-grid/tests/grid/grid_test.rs | 7 +- .../flowy-derive/src/proto_buf/deserialize.rs | 8 + .../src/entities/grid.rs | 7 +- .../src/protobuf/model/grid.rs | 135 +++++-------- .../src/protobuf/proto/grid.proto | 3 +- 27 files changed, 382 insertions(+), 485 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart index 07be14efa2..09b8bef584 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart @@ -16,7 +16,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; - +import 'dart:convert' show utf8; part 'cell_service.freezed.dart'; part 'data_loader.dart'; part 'context_builder.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index ed191c7d60..c6f71a2e32 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -1,6 +1,6 @@ part of 'cell_service.dart'; -typedef GridCellContext = _GridCellContext; +typedef GridCellContext = _GridCellContext; typedef GridSelectOptionCellContext = _GridCellContext; typedef GridDateCellContext = _GridCellContext; @@ -16,26 +16,33 @@ class GridCellContextBuilder { _GridCellContext build() { switch (_gridCell.field.fieldType) { case FieldType.Checkbox: + final cellDataLoader = GridCellDataLoader( + gridCell: _gridCell, + parser: StringCellDataParser(), + ); return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: GridCellDataLoader(gridCell: _gridCell), + cellDataLoader: cellDataLoader, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.DateTime: + final cellDataLoader = GridCellDataLoader( + gridCell: _gridCell, + parser: DateCellDataParser(), + ); + return GridDateCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: DateCellDataLoader(gridCell: _gridCell), + cellDataLoader: cellDataLoader, cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), ); case FieldType.Number: final cellDataLoader = GridCellDataLoader( gridCell: _gridCell, - config: const GridCellDataConfig( - reloadOnCellChanged: true, - reloadOnFieldChanged: true, - ), + parser: StringCellDataParser(), + config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true), ); return GridCellContext( gridCell: _gridCell, @@ -44,18 +51,28 @@ class GridCellContextBuilder { cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.RichText: + final cellDataLoader = GridCellDataLoader( + gridCell: _gridCell, + parser: StringCellDataParser(), + ); return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: GridCellDataLoader(gridCell: _gridCell), + cellDataLoader: cellDataLoader, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.MultiSelect: case FieldType.SingleSelect: + final cellDataLoader = GridCellDataLoader( + gridCell: _gridCell, + parser: SelectOptionCellDataParser(), + config: const GridCellDataConfig(reloadOnFieldChanged: true), + ); + return GridSelectOptionCellContext( gridCell: _gridCell, cellCache: _cellCache, - cellDataLoader: SelectOptionCellDataLoader(gridCell: _gridCell), + cellDataLoader: cellDataLoader, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); default: @@ -131,10 +148,7 @@ class _GridCellContext extends Equatable { } onCellChangedFn() { - final value = _cellDataNotifier.value; - if (value is T) { - onCellChanged(value); - } + onCellChanged(_cellDataNotifier.value as T); if (cellDataLoader.config.reloadOnCellChanged) { _loadData(); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart index 2e1801103e..4b66c08224 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart @@ -4,8 +4,8 @@ abstract class IGridCellDataConfig { // The cell data will reload if it receives the field's change notification. bool get reloadOnFieldChanged; - // The cell data will reload if it receives the cell's change notification. - // For example, the number cell should be reloaded after user input the number. + // When the reloadOnCellChanged is true, it will load the cell data after user input. + // For example: The number cell reload the cell data that carries the format // user input: 12 // cell display: $12 bool get reloadOnCellChanged; @@ -31,63 +31,44 @@ abstract class IGridCellDataLoader { } abstract class ICellDataParser { - T? parserData(); + T? parserData(List data); } -class GridCellDataLoader extends IGridCellDataLoader { +class GridCellDataLoader extends IGridCellDataLoader { final CellService service = CellService(); final GridCell gridCell; + final ICellDataParser parser; @override final IGridCellDataConfig config; GridCellDataLoader({ required this.gridCell, + required this.parser, this.config = const GridCellDataConfig(), }); @override - Future loadData() { + Future loadData() { final fut = service.getCell( gridId: gridCell.gridId, fieldId: gridCell.field.id, rowId: gridCell.rowId, ); - return fut.then((result) { - return result.fold((Cell data) => data.content, (err) { - Log.error(err); - return ""; - }); - }); - } -} - -class DateCellDataLoader extends IGridCellDataLoader { - final GridCell gridCell; - final IGridCellDataConfig _config; - DateCellDataLoader({ - required this.gridCell, - }) : _config = const GridCellDataConfig(reloadOnFieldChanged: true); - - @override - IGridCellDataConfig get config => _config; - - @override - Future loadData() { - final payload = CellIdentifierPayload.create() - ..gridId = gridCell.gridId - ..fieldId = gridCell.field.id - ..rowId = gridCell.rowId; - - return GridEventGetDateCellData(payload).send().then((result) { - return result.fold( - (data) => data, - (err) { - Log.error(err); + return fut.then( + (result) => result.fold((Cell cell) { + try { + return parser.parserData(cell.data); + } catch (e, s) { + Log.error('$parser parser cellData failed, $e'); + Log.error('Stack trace \n $s'); return null; - }, - ); - }); + } + }, (err) { + Log.error(err); + return null; + }), + ); } } @@ -113,3 +94,30 @@ class SelectOptionCellDataLoader extends IGridCellDataLoader const GridCellDataConfig(reloadOnFieldChanged: true); } + +class StringCellDataParser implements ICellDataParser { + @override + String? parserData(List data) { + return utf8.decode(data); + } +} + +class DateCellDataParser implements ICellDataParser { + @override + DateCellData? parserData(List data) { + if (data.isEmpty) { + return null; + } + return DateCellData.fromBuffer(data); + } +} + +class SelectOptionCellDataParser implements ICellDataParser { + @override + SelectOptionCellData? parserData(List data) { + if (data.isEmpty) { + return null; + } + return SelectOptionCellData.fromBuffer(data); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart index 056b65e556..0b6b1fd4ab 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart @@ -21,7 +21,6 @@ class SelectOptionCellBloc extends Bloc options, List selectedOptions, ) = _DidReceiveOptions; } @@ -66,7 +63,6 @@ class SelectOptionCellEvent with _$SelectOptionCellEvent { @freezed class SelectOptionCellState with _$SelectOptionCellState { const factory SelectOptionCellState({ - required List options, required List selectedOptions, }) = _SelectOptionCellState; @@ -74,7 +70,6 @@ class SelectOptionCellState with _$SelectOptionCellState { final data = context.getCellData(); return SelectOptionCellState( - options: data?.options ?? [], selectedOptions: data?.selectOptions ?? [], ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart index a1e1315682..d5065aa6fa 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart @@ -24,6 +24,7 @@ class SelectOptionCellEditorBloc extends Bloc filter, List allOptions) { final List options = List.from(allOptions); Option createOption = filter; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart index 2123802966..c29a14d696 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart @@ -13,19 +13,16 @@ class TextCellBloc extends Bloc { }) : super(TextCellState.initial(cellContext)) { on( (event, emit) async { - await event.map( - initial: (_InitialCell value) async { + await event.when( + initial: () async { _startListening(); }, - updateText: (_UpdateText value) { - cellContext.saveCellData(value.text); - emit(state.copyWith(content: value.text)); + updateText: (text) { + cellContext.saveCellData(text); + emit(state.copyWith(content: text)); }, - didReceiveCellData: (_DidReceiveCellData value) { - emit(state.copyWith(content: value.cellData.cell?.content ?? "")); - }, - didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith(content: value.cellContent)); + didReceiveCellUpdate: (content) { + emit(state.copyWith(content: content)); }, ); }, @@ -56,7 +53,6 @@ class TextCellBloc extends Bloc { @freezed class TextCellEvent with _$TextCellEvent { const factory TextCellEvent.initial() = _InitialCell; - const factory TextCellEvent.didReceiveCellData(GridCell cellData) = _DidReceiveCellData; const factory TextCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate; const factory TextCellEvent.updateText(String text) = _UpdateText; } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart index bf4964054e..0149f63b3a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart @@ -392,20 +392,3 @@ class GridEventUpdateDateCell { } } -class GridEventGetDateCellData { - CellIdentifierPayload request; - GridEventGetDateCellData(this.request); - - Future> send() { - final request = FFIRequest.create() - ..event = GridEvent.GetDateCellData.toString() - ..payload = requestToBytes(this.request); - - return Dispatch.asyncRequest(request) - .then((bytesResult) => bytesResult.fold( - (okBytes) => left(DateCellData.fromBuffer(okBytes)), - (errBytes) => right(FlowyError.fromBuffer(errBytes)), - )); - } -} - diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index 19a1aca54d..87f9036c88 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -1366,24 +1366,19 @@ class GridBlock extends $pb.GeneratedMessage { class Cell extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Cell', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') - ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') - ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY) ..hasRequiredFields = false ; Cell._() : super(); factory Cell({ $core.String? fieldId, - $core.String? content, $core.List<$core.int>? data, }) { final _result = create(); if (fieldId != null) { _result.fieldId = fieldId; } - if (content != null) { - _result.content = content; - } if (data != null) { _result.data = data; } @@ -1420,22 +1415,13 @@ class Cell extends $pb.GeneratedMessage { void clearFieldId() => clearField(1); @$pb.TagNumber(2) - $core.String get content => $_getSZ(1); + $core.List<$core.int> get data => $_getN(1); @$pb.TagNumber(2) - set content($core.String v) { $_setString(1, v); } + set data($core.List<$core.int> v) { $_setBytes(1, v); } @$pb.TagNumber(2) - $core.bool hasContent() => $_has(1); + $core.bool hasData() => $_has(1); @$pb.TagNumber(2) - void clearContent() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$core.int> get data => $_getN(2); - @$pb.TagNumber(3) - set data($core.List<$core.int> v) { $_setBytes(2, v); } - @$pb.TagNumber(3) - $core.bool hasData() => $_has(2); - @$pb.TagNumber(3) - void clearData() => clearField(3); + void clearData() => clearField(2); } class RepeatedCell extends $pb.GeneratedMessage { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index c203258374..68c1569c9c 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -289,13 +289,12 @@ const Cell$json = const { '1': 'Cell', '2': const [ const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'}, - const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, - const {'1': 'data', '3': 3, '4': 1, '5': 12, '10': 'data'}, + const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'}, ], }; /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSEgoEZGF0YRgDIAEoDFIEZGF0YQ=='); +final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhIKBGRhdGEYAiABKAxSBGRhdGE='); @$core.Deprecated('Use repeatedCellDescriptor instead') const RepeatedCell$json = const { '1': 'RepeatedCell', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart index 341dcf8d6a..863162cc3e 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart @@ -33,7 +33,6 @@ class GridEvent extends $pb.ProtobufEnum { static const GridEvent UpdateCell = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell'); static const GridEvent UpdateSelectOptionCell = GridEvent._(72, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOptionCell'); static const GridEvent UpdateDateCell = GridEvent._(80, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateDateCell'); - static const GridEvent GetDateCellData = GridEvent._(90, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetDateCellData'); static const $core.List values = [ GetGridData, @@ -59,7 +58,6 @@ class GridEvent extends $pb.ProtobufEnum { UpdateCell, UpdateSelectOptionCell, UpdateDateCell, - GetDateCellData, ]; static final $core.Map<$core.int, GridEvent> _byValue = $pb.ProtobufEnum.initByValue(values); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart index 58a712a4da..08de369a01 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart @@ -35,9 +35,8 @@ const GridEvent$json = const { const {'1': 'UpdateCell', '2': 71}, const {'1': 'UpdateSelectOptionCell', '2': 72}, const {'1': 'UpdateDateCell', '2': 80}, - const {'1': 'GetDateCellData', '2': 90}, ], }; /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFASEwoPR2V0RGF0ZUNlbGxEYXRhEFo='); +final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIZChVVcGRhdGVGaWVsZFR5cGVPcHRpb24QDBIPCgtJbnNlcnRGaWVsZBANEg8KC0RlbGV0ZUZpZWxkEA4SEQoNU3dpdGNoVG9GaWVsZBAUEhIKDkR1cGxpY2F0ZUZpZWxkEBUSDAoITW92ZUl0ZW0QFhIWChJHZXRGaWVsZFR5cGVPcHRpb24QFxIZChVDcmVhdGVGaWVsZFR5cGVPcHRpb24QGBITCg9OZXdTZWxlY3RPcHRpb24QHhIbChdHZXRTZWxlY3RPcHRpb25DZWxsRGF0YRAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlU2VsZWN0T3B0aW9uQ2VsbBBIEhIKDlVwZGF0ZURhdGVDZWxsEFA='); diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index cf5c095ee9..edc5082ba8 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -263,27 +263,6 @@ pub(crate) async fn update_cell_handler( Ok(()) } -#[tracing::instrument(level = "trace", skip(data, manager), err)] -pub(crate) async fn get_date_cell_data_handler( - data: Data, - manager: AppData>, -) -> DataResult { - let params: CellIdentifier = data.into_inner().try_into()?; - let editor = manager.get_grid_editor(¶ms.grid_id)?; - match editor.get_field_meta(¶ms.field_id).await { - None => { - tracing::error!("Can't find the corresponding field with id: {}", params.field_id); - data_result(DateCellData::default()) - } - Some(field_meta) => { - let cell_meta = editor.get_cell_meta(¶ms.row_id, ¶ms.field_id).await?; - let type_option = DateTypeOption::from(&field_meta); - let date_cell_data = type_option.make_date_cell_data(&cell_meta)?; - data_result(date_cell_data) - } - } -} - #[tracing::instrument(level = "trace", skip_all, err)] pub(crate) async fn new_select_option_handler( data: Data, diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index c740eee6d8..48e5fd9b6c 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -29,7 +29,6 @@ pub fn create(grid_manager: Arc) -> Module { // Cell .event(GridEvent::GetCell, get_cell_handler) .event(GridEvent::UpdateCell, update_cell_handler) - .event(GridEvent::GetDateCellData, get_date_cell_data_handler) // SelectOption .event(GridEvent::NewSelectOption, new_select_option_handler) .event(GridEvent::UpdateSelectOption, update_select_option_handler) @@ -112,7 +111,4 @@ pub enum GridEvent { #[event(input = "DateChangesetPayload")] UpdateDateCell = 80, - - #[event(input = "CellIdentifierPayload", output = "DateCellData")] - GetDateCellData = 90, } diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs index 6762254df7..73eaff920f 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs @@ -48,7 +48,6 @@ pub enum GridEvent { UpdateCell = 71, UpdateSelectOptionCell = 72, UpdateDateCell = 80, - GetDateCellData = 90, } impl ::protobuf::ProtobufEnum for GridEvent { @@ -81,7 +80,6 @@ impl ::protobuf::ProtobufEnum for GridEvent { 71 => ::std::option::Option::Some(GridEvent::UpdateCell), 72 => ::std::option::Option::Some(GridEvent::UpdateSelectOptionCell), 80 => ::std::option::Option::Some(GridEvent::UpdateDateCell), - 90 => ::std::option::Option::Some(GridEvent::GetDateCellData), _ => ::std::option::Option::None } } @@ -111,7 +109,6 @@ impl ::protobuf::ProtobufEnum for GridEvent { GridEvent::UpdateCell, GridEvent::UpdateSelectOptionCell, GridEvent::UpdateDateCell, - GridEvent::GetDateCellData, ]; values } @@ -140,7 +137,7 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fevent_map.proto*\xdc\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ + \n\x0fevent_map.proto*\xc7\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\ \x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\ \x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\ @@ -151,7 +148,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteR\ ow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\ \n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\ - \x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\ + \x0eUpdateDateCell\x10Pb\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto index 10e98a2934..623bf4b7da 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto @@ -24,5 +24,4 @@ enum GridEvent { UpdateCell = 71; UpdateSelectOptionCell = 72; UpdateDateCell = 80; - GetDateCellData = 90; } 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 90bb53cea6..c8d14b6b03 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 @@ -58,7 +58,7 @@ impl CellDataOperation for CheckboxTypeOption { let encoded_data = encoded_data.into(); if encoded_data == YES || encoded_data == NO { - return Ok(DecodedCellData::from_content(encoded_data)); + return Ok(DecodedCellData::new(encoded_data)); } Ok(DecodedCellData::default()) @@ -104,37 +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_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), YES ); let data = apply_cell_data_changeset("1", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), YES ); let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), YES ); let data = apply_cell_data_changeset("false", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), NO ); let data = apply_cell_data_changeset("no", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), NO ); let data = apply_cell_data_changeset("12", None, &field_meta).unwrap(); assert_eq!( - decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, + decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(), 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 5ecca97105..25a16a0515 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,9 +1,7 @@ use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{ - CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData, TypeOptionCellData, -}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; use chrono::NaiveDateTime; @@ -79,32 +77,12 @@ impl DateTypeOption { } } - pub fn make_date_cell_data(&self, cell_meta: &Option) -> FlowyResult { - if cell_meta.is_none() { - return Ok(DateCellData::default()); - } - - let json = &cell_meta.as_ref().unwrap().data; - let result = TypeOptionCellData::from_str(json); - if result.is_err() { - return Ok(DateCellData::default()); - } - - 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_else(|| "".to_owned()); - let timestamp = serde_cell_data.timestamp; - - Ok(DateCellData { date, time, timestamp }) - } - - fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData { + fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String { if serde_cell_data.timestamp == 0 { - return DecodedCellData::default(); + return "".to_owned(); } - let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time); - DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content) + self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time) } fn timestamp_from_utc_with_time( @@ -156,7 +134,11 @@ impl CellDataOperation, DateCellDataSerde> fo } let encoded_data = encoded_data.into().try_into_inner()?; - Ok(self.decode_cell_data_from_timestamp(&encoded_data)) + let date = self.date_desc_from_timestamp(&encoded_data); + let time = encoded_data.time.unwrap_or_else(|| "".to_owned()); + let timestamp = encoded_data.timestamp; + + DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp }) } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -417,20 +399,27 @@ impl std::convert::From for CellContentChangeset { #[cfg(test)] mod tests { use crate::services::field::FieldBuilder; - 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::field::{ + DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, }; + use crate::services::row::{CellDataOperation, EncodedCellData}; use flowy_grid_data_model::entities::{FieldMeta, FieldType}; use strum::IntoEnumIterator; #[test] fn date_description_invalid_input_test() { - 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_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content, - "".to_owned() + let type_option = DateTypeOption::default(); + let field_type = FieldType::DateTime; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some("1e".to_string()), + time: Some("23:00".to_owned()), + }, + &field_type, + &field_meta, + "", ); } @@ -442,40 +431,16 @@ mod tests { 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), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); + assert_decode_timestamp(1647251762, &type_option, &field_meta, "Mar 14,2022"); } DateFormat::US => { - assert_eq!( - "2022/03/14".to_owned(), - type_option - .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); + assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14"); } DateFormat::ISO => { - assert_eq!( - "2022-03-14".to_owned(), - type_option - .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); + assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022-03-14"); } DateFormat::Local => { - assert_eq!( - "2022/03/14".to_owned(), - type_option - .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); + assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14"); } } } @@ -483,43 +448,6 @@ mod tests { #[test] fn date_description_time_format_test() { - let mut type_option = DateTypeOption::default(); - 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 { - TimeFormat::TwentyFourHour => { - assert_eq!( - "Mar 14,2022".to_owned(), - type_option.today_desc_from_timestamp(1647251762, &None) - ); - assert_eq!( - "Mar 14,2022".to_owned(), - type_option - .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); - } - TimeFormat::TwelveHour => { - assert_eq!( - "Mar 14,2022".to_owned(), - type_option.today_desc_from_timestamp(1647251762, &None) - ); - assert_eq!( - "Mar 14,2022".to_owned(), - type_option - .decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta) - .unwrap() - .content - ); - } - } - } - } - - #[test] - fn date_description_time_format_test2() { let mut type_option = DateTypeOption::default(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); @@ -529,7 +457,7 @@ mod tests { type_option.include_time = true; match time_format { TimeFormat::TwentyFourHour => { - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(1653609600.to_string()), @@ -539,7 +467,7 @@ mod tests { &field_meta, "May 27,2022 00:00", ); - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(1653609600.to_string()), @@ -551,7 +479,7 @@ mod tests { ); } TimeFormat::TwelveHour => { - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(1653609600.to_string()), @@ -562,7 +490,7 @@ mod tests { "May 27,2022 12:00 AM", ); - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(1653609600.to_string()), @@ -573,7 +501,7 @@ mod tests { "May 27,2022", ); - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(1653609600.to_string()), @@ -595,7 +523,7 @@ mod tests { let field_meta = FieldBuilder::from_field_type(&field_type).build(); let date_timestamp = "1653609600".to_owned(); - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(date_timestamp.clone()), @@ -607,7 +535,7 @@ mod tests { ); type_option.include_time = true; - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(date_timestamp.clone()), @@ -618,7 +546,7 @@ mod tests { "May 27,2022 00:00", ); - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(date_timestamp.clone()), @@ -630,7 +558,7 @@ mod tests { ); type_option.time_format = TimeFormat::TwelveHour; - assert_result( + assert_changeset_result( &type_option, DateCellContentChangeset { date: Some(date_timestamp), @@ -670,22 +598,39 @@ mod tests { 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( + fn assert_changeset_result( type_option: &DateTypeOption, changeset: DateCellContentChangeset, - field_type: &FieldType, + _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) + assert_eq!( + expected.to_owned(), + decode_cell_data(encoded_data, type_option, field_meta) + ); + } + + fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) { + let serde_json = DateCellDataSerde { timestamp, time: None }.to_string(); + + assert_eq!( + expected.to_owned(), + decode_cell_data(serde_json, type_option, field_meta) + ); + } + + fn decode_cell_data>>( + encoded_data: T, + type_option: &DateTypeOption, + field_meta: &FieldMeta, + ) -> String { + type_option + .decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta) .unwrap() - .content; - assert_eq!(expected.to_owned(), content); + .parse::() + .unwrap() + .date } } 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 d500bbcc39..b889c1c2c0 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 @@ -94,22 +94,22 @@ impl CellDataOperation for NumberTypeOption { match self.format { NumberFormat::Number => { if let Ok(v) = cell_data.parse::() { - return Ok(DecodedCellData::from_content(v.to_string())); + return Ok(DecodedCellData::new(v.to_string())); } if let Ok(v) = cell_data.parse::() { - return Ok(DecodedCellData::from_content(v.to_string())); + return Ok(DecodedCellData::new(v.to_string())); } Ok(DecodedCellData::default()) } NumberFormat::Percent => { let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); - Ok(DecodedCellData::from_content(content)) + Ok(DecodedCellData::new(content)) } _ => { let content = self.money_from_str(&cell_data); - Ok(DecodedCellData::from_content(content)) + Ok(DecodedCellData::new(content)) } } } @@ -738,7 +738,7 @@ mod tests { type_option .decode_cell_data(data(cell_data), field_type, field_meta) .unwrap() - .content, + .to_string(), expected_str.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 c73dd1802f..1e857e9fd3 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 @@ -109,16 +109,18 @@ impl CellDataOperation for SingleSelectTypeOption { return Ok(DecodedCellData::default()); } - 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()) + let encoded_data = encoded_data.into(); + let mut cell_data = SelectOptionCellData { + options: self.options.clone(), + select_options: vec![], + }; + if let Some(option_id) = select_option_ids(encoded_data).first() { + if let Some(option) = self.options.iter().find(|option| &option.id == option_id) { + cell_data.select_options.push(option.clone()); + } } + + DecodedCellData::try_from_bytes(cell_data) } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -204,17 +206,21 @@ impl CellDataOperation for MultiSelectTypeOption { if !decoded_field_type.is_select_option() { return Ok(DecodedCellData::default()); } - let cell_data = encoded_data.into(); - let option_ids = select_option_ids(cell_data); - let content = self - .options - .iter() - .filter(|option| option_ids.contains(&option.id)) - .map(|option| option.name.clone()) - .collect::>() - .join(SELECTION_IDS_SEPARATOR); - Ok(DecodedCellData::from_content(content)) + tracing::info!("😁{}", self.options.len()); + + let encoded_data = encoded_data.into(); + let select_options = select_option_ids(encoded_data) + .into_iter() + .flat_map(|option_id| self.options.iter().find(|option| option.id == option_id).cloned()) + .collect::>(); + + let cell_data = SelectOptionCellData { + options: self.options.clone(), + select_options, + }; + + DecodedCellData::try_from_bytes(cell_data) } fn apply_changeset(&self, changeset: T, cell_meta: Option) -> Result @@ -280,7 +286,7 @@ fn select_option_ids(data: String) -> Vec { .collect::>() } -#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)] pub struct SelectOption { #[pb(index = 1)] pub id: String, @@ -446,7 +452,7 @@ pub struct SelectOptionCellData { pub select_options: Vec, } -#[derive(ProtoBuf_Enum, Serialize, Deserialize, Debug, Clone)] +#[derive(ProtoBuf_Enum, PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] #[repr(u8)] pub enum SelectOptionColor { Purple = 0, @@ -502,9 +508,10 @@ mod tests { use crate::services::field::FieldBuilder; use crate::services::field::{ MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset, - SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, + SelectOptionCellData, SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR, }; use crate::services::row::CellDataOperation; + use flowy_grid_data_model::entities::FieldMeta; #[test] fn single_select_test() { @@ -526,47 +533,24 @@ mod tests { 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, - ); + assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]); 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, - ); + assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]); // 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, - "", - ); + assert_single_select_options(cell_data, &type_option, &field_meta, vec![]); // 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, - "", - ); + + assert_single_select_options(cell_data, &type_option, &field_meta, vec![]); // Invalid changeset assert!(type_option.apply_changeset("123", None).is_err()); @@ -592,49 +576,64 @@ mod tests { 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), + assert_multi_select_options( + cell_data, + &type_option, + &field_meta, + vec![google_option.clone(), facebook_option.clone()], ); 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, - ); + assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]); // 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, - "", - ); + assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]); // 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, - "", - ); + assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]); // Invalid changeset assert!(type_option.apply_changeset("123", None).is_err()); } + + fn assert_multi_select_options( + cell_data: String, + type_option: &MultiSelectTypeOption, + field_meta: &FieldMeta, + expected: Vec, + ) { + assert_eq!( + expected, + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .parse::() + .unwrap() + .select_options, + ); + } + + fn assert_single_select_options( + cell_data: String, + type_option: &SingleSelectTypeOption, + field_meta: &FieldMeta, + expected: Vec, + ) { + assert_eq!( + expected, + type_option + .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .unwrap() + .parse::() + .unwrap() + .select_options, + ); + } } 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 32d45a3e29..94c55e3664 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 @@ -49,7 +49,7 @@ impl CellDataOperation for RichTextTypeOption { decode_cell_data(encoded_data, decoded_field_type, decoded_field_type, field_meta) } else { let cell_data = encoded_data.into(); - Ok(DecodedCellData::from_content(cell_data)) + Ok(DecodedCellData::new(cell_data)) } } @@ -85,22 +85,26 @@ mod tests { type_option .decode_cell_data(json, &field_type, &date_time_field_meta) .unwrap() - .content, + .parse::() + .unwrap() + .date, "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 = SingleSelectTypeOptionBuilder::default().option(done_option.clone()); 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() + .parse::() + .unwrap() + .select_options, + vec![done_option], ); // Multiple select @@ -109,8 +113,8 @@ mod tests { 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); + .option(google_option.clone()) + .option(facebook_option.clone()); 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(); @@ -118,8 +122,10 @@ mod tests { type_option .decode_cell_data(cell_data, &FieldType::MultiSelect, &multi_select_field_meta) .unwrap() - .content, - "Google,Facebook".to_owned() + .parse::() + .unwrap() + .select_options, + vec![google_option, facebook_option] ); //Number @@ -129,7 +135,7 @@ mod tests { type_option .decode_cell_data("18443".to_owned(), &FieldType::Number, &number_field_meta) .unwrap() - .content, + .to_string(), "$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 f92e4ac8d2..0ecc9198dd 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,5 +1,6 @@ use crate::services::field::*; -use flowy_error::{ErrorCode, FlowyError, FlowyResult}; +use bytes::Bytes; +use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType}; use serde::{Deserialize, Serialize}; use std::fmt::Formatter; @@ -145,8 +146,15 @@ pub fn decode_cell_data_from_type_option_cell_data DecodedCellData { if let Ok(type_option_cell_data) = data.try_into() { 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() + match decode_cell_data(encoded_data, &s_field_type, field_type, field_meta) { + Ok(cell_data) => cell_data, + Err(e) => { + tracing::error!("Decode cell data failed, {:?}", e); + DecodedCellData::default() + } + } } else { + tracing::error!("Decode type option data failed"); DecodedCellData::default() } } @@ -158,6 +166,7 @@ pub fn decode_cell_data>( field_meta: &FieldMeta, ) -> FlowyResult { let encoded_data = encoded_data.into(); + tracing::info!("😁{:?}", field_meta.type_options); let get_cell_data = || { let data = match t_field_type { FieldType::RichText => field_meta @@ -183,13 +192,7 @@ pub fn decode_cell_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(Ok(data)) => Ok(data), Some(Err(err)) => { tracing::error!("{:?}", err); Ok(DecodedCellData::default()) @@ -227,23 +230,39 @@ where #[derive(Default)] pub struct DecodedCellData { pub data: Vec, - pub content: String, } impl DecodedCellData { - pub fn from_content(content: String) -> Self { + pub fn new>(data: T) -> Self { Self { - data: content.as_bytes().to_vec(), - content, + data: data.as_ref().to_vec(), } } - pub fn new>(data: T, content: String) -> Self { - let data = data.as_ref().to_vec(); - Self { data, content } + pub fn try_from_bytes>(bytes: T) -> FlowyResult + where + >::Error: std::fmt::Debug, + { + let bytes = bytes.try_into().map_err(internal_error)?; + Ok(Self { data: bytes.to_vec() }) } - pub fn split(self) -> (Vec, String) { - (self.data, self.content) + pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult + where + >::Error: std::fmt::Debug, + { + T::try_from(self.data.as_ref()).map_err(internal_error) + } +} + +impl ToString for DecodedCellData { + fn to_string(&self) -> String { + match String::from_utf8(self.data.clone()) { + Ok(s) => s, + Err(e) => { + tracing::error!("DecodedCellData to string failed: {:?}", e); + "".to_string() + } + } } } 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 af4af0e491..865058ffb6 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 @@ -31,17 +31,15 @@ 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_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split(); - let cell = Cell::new(&field_id, content, raw); + let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data; + let cell = Cell::new(&field_id, data); 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_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split(); - Some(Cell::new(field_id, content, raw)) + let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data; + Some(Cell::new(field_id, data)) } pub(crate) fn make_row_orders_from_row_metas(row_metas: &[Arc]) -> Vec { 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 f4549cb726..afe89d4623 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs @@ -2,7 +2,7 @@ use crate::grid::script::EditorScript::*; use crate::grid::script::*; use chrono::NaiveDateTime; use flowy_grid::services::field::{ - DateCellContentChangeset, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset, + DateCellContentChangeset, DateCellData, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR, }; use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder}; @@ -292,8 +292,9 @@ async fn grid_row_add_date_cell_test() { let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone(); assert_eq!( decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type) - .split() - .1, + .parse::() + .unwrap() + .date, "2022/03/16", ); let scripts = vec![CreateRow { context }]; diff --git a/shared-lib/flowy-derive/src/proto_buf/deserialize.rs b/shared-lib/flowy-derive/src/proto_buf/deserialize.rs index e09f7ea847..f0325db3b0 100644 --- a/shared-lib/flowy-derive/src/proto_buf/deserialize.rs +++ b/shared-lib/flowy-derive/src/proto_buf/deserialize.rs @@ -30,6 +30,14 @@ pub fn make_de_token_steam(ctxt: &Ctxt, ast: &ASTContainer) -> Option for #struct_ident { + type Error = ::protobuf::ProtobufError; + fn try_from(bytes: &[u8]) -> Result { + let pb: crate::protobuf::#pb_ty = ::protobuf::Message::parse_from_bytes(bytes)?; + #struct_ident::try_from(pb) + } + } + impl std::convert::TryFrom for #struct_ident { type Error = ::protobuf::ProtobufError; fn try_from(mut pb: crate::protobuf::#pb_ty) -> Result { 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 dab20b772c..944f52a81f 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -484,17 +484,13 @@ pub struct Cell { pub field_id: String, #[pb(index = 2)] - pub content: String, - - #[pb(index = 3)] pub data: Vec, } impl Cell { - pub fn new(field_id: &str, content: String, data: Vec) -> Self { + pub fn new(field_id: &str, data: Vec) -> Self { Self { field_id: field_id.to_owned(), - content, data, } } @@ -502,7 +498,6 @@ impl Cell { pub fn empty(field_id: &str) -> Self { Self { field_id: field_id.to_owned(), - content: "".to_string(), data: vec![], } } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index e5f9102fd5..a063df9d04 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -4743,7 +4743,6 @@ impl ::protobuf::reflect::ProtobufValue for GridBlock { pub struct Cell { // message fields pub field_id: ::std::string::String, - pub content: ::std::string::String, pub data: ::std::vec::Vec, // special fields pub unknown_fields: ::protobuf::UnknownFields, @@ -4787,33 +4786,7 @@ impl Cell { ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) } - // string content = 2; - - - pub fn get_content(&self) -> &str { - &self.content - } - pub fn clear_content(&mut self) { - self.content.clear(); - } - - // Param is passed by value, moved - pub fn set_content(&mut self, v: ::std::string::String) { - self.content = v; - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_content(&mut self) -> &mut ::std::string::String { - &mut self.content - } - - // Take field - pub fn take_content(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.content, ::std::string::String::new()) - } - - // bytes data = 3; + // bytes data = 2; pub fn get_data(&self) -> &[u8] { @@ -4853,9 +4826,6 @@ impl ::protobuf::Message for Cell { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; }, 2 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; - }, - 3 => { ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?; }, _ => { @@ -4873,11 +4843,8 @@ impl ::protobuf::Message for Cell { if !self.field_id.is_empty() { my_size += ::protobuf::rt::string_size(1, &self.field_id); } - if !self.content.is_empty() { - my_size += ::protobuf::rt::string_size(2, &self.content); - } if !self.data.is_empty() { - my_size += ::protobuf::rt::bytes_size(3, &self.data); + my_size += ::protobuf::rt::bytes_size(2, &self.data); } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); @@ -4888,11 +4855,8 @@ impl ::protobuf::Message for Cell { if !self.field_id.is_empty() { os.write_string(1, &self.field_id)?; } - if !self.content.is_empty() { - os.write_string(2, &self.content)?; - } if !self.data.is_empty() { - os.write_bytes(3, &self.data)?; + os.write_bytes(2, &self.data)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -4937,11 +4901,6 @@ impl ::protobuf::Message for Cell { |m: &Cell| { &m.field_id }, |m: &mut Cell| { &mut m.field_id }, )); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "content", - |m: &Cell| { &m.content }, - |m: &mut Cell| { &mut m.content }, - )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>( "data", |m: &Cell| { &m.data }, @@ -4964,7 +4923,6 @@ impl ::protobuf::Message for Cell { impl ::protobuf::Clear for Cell { fn clear(&mut self) { self.field_id.clear(); - self.content.clear(); self.data.clear(); self.unknown_fields.clear(); } @@ -8340,50 +8298,49 @@ static file_descriptor_proto_data: &'static [u8] = b"\ derR\x0bdeletedRows\x123\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\x10.Upd\ atedRowOrderR\x0bupdatedRows\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\ \x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\ - Orders\"O\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\ - \x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\x12\x12\n\x04data\ - \x18\x03\x20\x01(\x0cR\x04data\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\ - \x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\ - \x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\ - \x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05\ - value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\ - \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\ - \x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\ - \n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gri\ - dId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\ - \x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0es\ - tart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_st\ - art_field_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\x07grid_id\ - \x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\ - \x07fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOpti\ - onData\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\t\ - R\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFie\ - ldOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid\ - _id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03\ - (\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetP\ - ayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\ - \x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\ - \x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\ - \x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\ - \x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvis\ - ibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\ - \x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\ - \x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_des\ - cB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_vis\ - ibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\ - \x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\ - idId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_\ - index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\ - \x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTy\ - peR\x02ty\"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\ - \x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\ - \x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_content\ - _changeset\x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\x1done_\ - of_cell_content_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\0\ - \x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\ - \x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSi\ - ngleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbo\ - x\x10\x05b\x06proto3\ + Orders\"5\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\ + \x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\"+\n\x0cRepeatedCell\ + \x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11Cre\ + ateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06\ + GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlock\ + Id\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayl\ + oad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_\ + row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\ + \"\xb6\x01\n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\ + \tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05fie\ + ld\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\ + \x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\ + \x15one_of_start_field_id\"|\n\x1cUpdateFieldTypeOptionPayload\x12\x17\n\ + \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\ + \x20\x01(\tR\x07fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\ + \x0etypeOptionData\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\ + \x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\ + \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orde\ + rs\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\ + \x15FieldChangesetPayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07f\ + ieldId\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04n\ + ame\x18\x03\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\ + \x01R\x04desc\x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\ + \tfieldType\x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\ + \x20\n\nvisibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05w\ + idth\x18\x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_option_data\x18\ + \t\x20\x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_\ + of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_\ + of_visibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\ + \x9c\x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ + \x06gridId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\ + \nfrom_index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\ + \x04\x20\x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.Mo\ + veItemTypeR\x02ty\"\xb3\x01\n\rCellChangeset\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05ro\ + wId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x126\n\x16cell_\ + content_changeset\x18\x04\x20\x01(\tH\0R\x14cellContentChangesetB\x1f\n\ + \x1done_of_cell_content_changeset**\n\x0cMoveItemType\x12\r\n\tMoveField\ + \x10\0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\ + \x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\ + \x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08C\ + heckbox\x10\x05b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index d762e65e70..18abbd8e54 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -95,8 +95,7 @@ message GridBlock { } message Cell { string field_id = 1; - string content = 2; - bytes data = 3; + bytes data = 2; } message RepeatedCell { repeated Cell items = 1; From b8582b9b6845293bb9d77677e7465ac7cfd7b848 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 28 May 2022 15:55:43 +0800 Subject: [PATCH 31/82] chore: set loaded data in SelectOptionCellEditorBloc --- .../cell/cell_service/context_builder.dart | 4 +- .../grid/cell/select_option_editor_bloc.dart | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index c6f71a2e32..aad8137f2e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -163,9 +163,9 @@ class _GridCellContext extends Equatable { _cellDataNotifier.removeListener(fn); } - T? getCellData() { + T? getCellData({bool loadIfNoCache = true}) { final data = cellCache.get(_cacheKey); - if (data == null) { + if (data == null && loadIfNoCache) { _loadData(); } return data; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart index d5065aa6fa..87eabdf759 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; @@ -13,11 +14,14 @@ part 'select_option_editor_bloc.freezed.dart'; class SelectOptionCellEditorBloc extends Bloc { final SelectOptionService _selectOptionService; final GridSelectOptionCellContext cellContext; + late final GridFieldsListener _fieldListener; void Function()? _onCellChangedFn; + Timer? _delayOperation; SelectOptionCellEditorBloc({ required this.cellContext, }) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell), + _fieldListener = GridFieldsListener(gridId: cellContext.gridId), super(SelectOptionEditorState.initial(cellContext)) { on( (event, emit) async { @@ -64,6 +68,8 @@ class SelectOptionCellEditorBloc extends Bloc add(SelectOptionEditorEvent.didReceiveOptions(data.options, data.selectOptions)), (err) { Log.error(err); return null; }, ); }); - } + }); } _MakeOptionResult _makeOptions(Option filter, List allOptions) { @@ -156,13 +160,21 @@ class SelectOptionCellEditorBloc extends Bloc Log.error(err), + ); + }); } } @@ -189,7 +201,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState { }) = _SelectOptionEditorState; factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) { - final data = context.getCellData(); + final data = context.getCellData(loadIfNoCache: false); return SelectOptionEditorState( options: data?.options ?? [], allOptions: data?.options ?? [], From ae0f71b5ee8c1938c69dc7d659e2a56324dab188 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 29 May 2022 10:56:34 +0800 Subject: [PATCH 32/82] chore: return URLCellData & parse url from cell content --- .../grid/cell/cell_service/cell_service.dart | 1 + .../cell/cell_service/context_builder.dart | 2 +- .../grid/cell/cell_service/data_loader.dart | 21 ++- .../application/grid/cell/url_cell_bloc.dart | 11 +- frontend/rust-lib/Cargo.lock | 2 +- frontend/rust-lib/flowy-grid/Cargo.toml | 2 +- .../field/type_options/date_type_option.rs | 2 +- .../type_options/selection_type_option.rs | 10 +- .../field/type_options/url_type_option.rs | 125 ++++++++++++++---- 9 files changed, 124 insertions(+), 52 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart index dea8e87712..68f8eada78 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart @@ -10,6 +10,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 709df1cb97..e1e7b949f1 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -3,7 +3,7 @@ part of 'cell_service.dart'; typedef GridCellContext = _GridCellContext; typedef GridSelectOptionCellContext = _GridCellContext; typedef GridDateCellContext = _GridCellContext; -typedef GridURLCellContext = _GridCellContext; +typedef GridURLCellContext = _GridCellContext; class GridCellContextBuilder { final GridCellCache _cellCache; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart index c223347715..92caedc4e9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart @@ -58,7 +58,11 @@ class GridCellDataLoader extends IGridCellDataLoader { return fut.then( (result) => result.fold((Cell cell) { try { - return parser.parserData(cell.data); + if (cell.data.isEmpty) { + return null; + } else { + return parser.parserData(cell.data); + } } catch (e, s) { Log.error('$parser parser cellData failed, $e'); Log.error('Stack trace \n $s'); @@ -105,9 +109,6 @@ class StringCellDataParser implements ICellDataParser { class DateCellDataParser implements ICellDataParser { @override DateCellData? parserData(List data) { - if (data.isEmpty) { - return null; - } return DateCellData.fromBuffer(data); } } @@ -115,19 +116,13 @@ class DateCellDataParser implements ICellDataParser { class SelectOptionCellDataParser implements ICellDataParser { @override SelectOptionCellData? parserData(List data) { - if (data.isEmpty) { - return null; - } return SelectOptionCellData.fromBuffer(data); } } -class URLCellDataParser implements ICellDataParser { +class URLCellDataParser implements ICellDataParser { @override - Cell? parserData(List data) { - if (data.isEmpty) { - return null; - } - return Cell.fromBuffer(data); + URLCellData? parserData(List data) { + return URLCellData.fromBuffer(data); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart index 8b4245540a..04284b16f7 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -23,7 +23,7 @@ class URLCellBloc extends Bloc { emit(state.copyWith(content: text)); }, didReceiveCellUpdate: (cellData) { - emit(state.copyWith(content: cellData.content)); + emit(state.copyWith(content: cellData.content, url: cellData.url)); }, ); }, @@ -54,7 +54,7 @@ class URLCellBloc extends Bloc { @freezed class URLCellEvent with _$URLCellEvent { const factory URLCellEvent.initial() = _InitialCell; - const factory URLCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; + const factory URLCellEvent.didReceiveCellUpdate(URLCellData cell) = _DidReceiveCellUpdate; const factory URLCellEvent.updateText(String text) = _UpdateText; } @@ -67,6 +67,9 @@ class URLCellState with _$URLCellState { factory URLCellState.initial(GridURLCellContext context) { final cellData = context.getCellData(); - return URLCellState(content: cellData?.content ?? "", url: ""); + return URLCellState( + content: cellData?.content ?? "", + url: cellData?.url ?? "", + ); } } diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 23fe8ed62b..59ca0e3dec 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -928,6 +928,7 @@ dependencies = [ "dart-notify", "dashmap", "diesel", + "fancy-regex", "flowy-database", "flowy-derive", "flowy-error", @@ -953,7 +954,6 @@ dependencies = [ "strum_macros", "tokio", "tracing", - "url", ] [[package]] diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index 2bde4a3f36..a4677d727f 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -35,7 +35,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0"} serde_repr = "0.1" indexmap = {version = "1.8.1", features = ["serde"]} -url = { version = "2"} +fancy-regex = "0.10.0" [dev-dependencies] flowy-test = { path = "../flowy-test" } 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 25a16a0515..c96166272a 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 @@ -627,7 +627,7 @@ mod tests { field_meta: &FieldMeta, ) -> String { type_option - .decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta) + .decode_cell_data(encoded_data, &FieldType::DateTime, field_meta) .unwrap() .parse::() .unwrap() 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 1e857e9fd3..26efd34acc 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 @@ -537,7 +537,7 @@ mod tests { let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]); + assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option]); // Invalid option id let cell_data = type_option @@ -580,12 +580,12 @@ mod tests { cell_data, &type_option, &field_meta, - vec![google_option.clone(), facebook_option.clone()], + vec![google_option.clone(), facebook_option], ); let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str(); let cell_data = type_option.apply_changeset(data, None).unwrap(); - assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]); + assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option]); // Invalid option id let cell_data = type_option @@ -612,7 +612,7 @@ mod tests { assert_eq!( expected, type_option - .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .decode_cell_data(cell_data, &field_meta.field_type, field_meta) .unwrap() .parse::() .unwrap() @@ -629,7 +629,7 @@ mod tests { assert_eq!( expected, type_option - .decode_cell_data(cell_data, &field_meta.field_type, &field_meta) + .decode_cell_data(cell_data, &field_meta.field_type, field_meta) .unwrap() .parse::() .unwrap() diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 5ca29ee87d..6cef992fbd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -1,14 +1,16 @@ use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; use bytes::Bytes; +use fancy_regex::Regex; use flowy_derive::ProtoBuf; -use flowy_error::{FlowyError, FlowyResult}; +use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; - +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; +use std::str::FromStr; #[derive(Default)] pub struct URLTypeOptionBuilder(URLTypeOption); @@ -32,7 +34,7 @@ pub struct URLTypeOption { } impl_type_option!(URLTypeOption, FieldType::URL); -impl CellDataOperation for URLTypeOption { +impl CellDataOperation, String> for URLTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -40,14 +42,13 @@ impl CellDataOperation for URLTypeOption { _field_meta: &FieldMeta, ) -> FlowyResult where - T: Into, + T: Into>, { if !decoded_field_type.is_url() { return Ok(DecodedCellData::default()); } - - let cell_data = encoded_data.into(); - Ok(DecodedCellData::from_content(cell_data)) + let cell_data = encoded_data.into().try_into_inner()?; + DecodedCellData::try_from_bytes(cell_data) } fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result @@ -55,7 +56,16 @@ impl CellDataOperation for URLTypeOption { C: Into, { let changeset = changeset.into(); - Ok(changeset.to_string()) + let mut cell_data = URLCellData { + url: "".to_string(), + content: changeset.to_string(), + }; + + if let Ok(Some(m)) = URL_REGEX.find(&changeset) { + cell_data.url = m.as_str().to_string(); + } + + cell_data.to_json() } } @@ -68,34 +78,97 @@ pub struct URLCellData { pub content: String, } +impl URLCellData { + pub fn new(s: &str) -> Self { + Self { + url: "".to_string(), + content: s.to_string(), + } + } + + fn to_json(&self) -> FlowyResult { + serde_json::to_string(self).map_err(internal_error) + } +} + +impl FromStr for URLCellData { + type Err = FlowyError; + + fn from_str(s: &str) -> Result { + serde_json::from_str::(s).map_err(internal_error) + } +} + +lazy_static! { + static ref URL_REGEX: Regex = Regex::new( + "[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)" + ) + .unwrap(); +} + #[cfg(test)] mod tests { use crate::services::field::FieldBuilder; - use crate::services::field::URLTypeOption; - use crate::services::row::CellDataOperation; + use crate::services::field::{URLCellData, URLTypeOption}; + use crate::services::row::{CellDataOperation, EncodedCellData}; use flowy_grid_data_model::entities::{FieldMeta, FieldType}; #[test] - fn url_type_option_format_test() { + fn url_type_option_test_no_url() { let type_option = URLTypeOption::default(); let field_type = FieldType::URL; let field_meta = FieldBuilder::from_field_type(&field_type).build(); - assert_equal(&type_option, "123", "123", &field_type, &field_meta); + assert_changeset(&type_option, "123", &field_type, &field_meta, "123", ""); } - fn assert_equal( - type_option: &URLTypeOption, - cell_data: &str, - expected_str: &str, - field_type: &FieldType, - field_meta: &FieldMeta, - ) { - assert_eq!( - type_option - .decode_cell_data(cell_data, field_type, field_meta) - .unwrap() - .content, - expected_str.to_owned() + #[test] + fn url_type_option_test_contains_url() { + let type_option = URLTypeOption::default(); + let field_type = FieldType::URL; + let field_meta = FieldBuilder::from_field_type(&field_type).build(); + assert_changeset( + &type_option, + "AppFlowy website - https://www.appflowy.io", + &field_type, + &field_meta, + "AppFlowy website - https://www.appflowy.io", + "https://www.appflowy.io", + ); + + assert_changeset( + &type_option, + "AppFlowy website appflowy.io", + &field_type, + &field_meta, + "AppFlowy website appflowy.io", + "appflowy.io", ); } + + fn assert_changeset( + type_option: &URLTypeOption, + cell_data: &str, + field_type: &FieldType, + field_meta: &FieldMeta, + expected: &str, + expected_url: &str, + ) { + let encoded_data = type_option.apply_changeset(cell_data, None).unwrap(); + let decode_cell_data = decode_cell_data(encoded_data, type_option, field_meta, field_type); + assert_eq!(expected.to_owned(), decode_cell_data.content); + assert_eq!(expected_url.to_owned(), decode_cell_data.url); + } + + fn decode_cell_data>>( + encoded_data: T, + type_option: &URLTypeOption, + field_meta: &FieldMeta, + field_type: &FieldType, + ) -> URLCellData { + type_option + .decode_cell_data(encoded_data, field_type, field_meta) + .unwrap() + .parse::() + .unwrap() + } } From 4a9627b31d02fda3812513fbc17ceda7afe83015 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 29 May 2022 21:47:38 +0800 Subject: [PATCH 33/82] chore: support open url with https scheme --- .../cell/cell_service/context_builder.dart | 31 +++-- .../grid/cell/checkbox_cell_bloc.dart | 2 +- .../application/grid/cell/date_cal_bloc.dart | 43 +++--- .../application/grid/cell/date_cell_bloc.dart | 10 +- .../grid/cell/number_cell_bloc.dart | 4 +- .../grid/cell/select_option_cell_bloc.dart | 2 +- .../application/grid/cell/text_cell_bloc.dart | 2 +- .../application/grid/cell/url_cell_bloc.dart | 12 +- .../grid/cell/url_cell_editor_bloc.dart | 73 ++++++++++ .../grid/src/widgets/cell/cell_builder.dart | 9 +- .../grid/src/widgets/cell/url_cell.dart | 127 ----------------- .../widgets/cell/url_cell/cell_editor.dart | 96 +++++++++++++ .../src/widgets/cell/url_cell/url_cell.dart | 131 ++++++++++++++++++ .../grid/src/widgets/row/grid_row.dart | 5 +- .../grid/src/widgets/row/row_detail.dart | 2 +- frontend/rust-lib/Cargo.lock | 1 + frontend/rust-lib/flowy-grid/Cargo.toml | 1 + .../type_options/selection_type_option.rs | 2 - .../field/type_options/url_type_option.rs | 18 ++- .../src/services/row/cell_data_operation.rs | 1 - 20 files changed, 388 insertions(+), 184 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart delete mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index e1e7b949f1..e4141c3e16 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -108,7 +108,8 @@ class _GridCellContext extends Equatable { late final ValueNotifier _cellDataNotifier; bool isListening = false; VoidCallback? _onFieldChangedFn; - Timer? _delayOperation; + Timer? _loadDataOperation; + Timer? _saveDataOperation; _GridCellContext({ required this.gridCell, @@ -138,7 +139,7 @@ class _GridCellContext extends Equatable { FieldType get fieldType => gridCell.field.fieldType; - VoidCallback? startListening({required void Function(T) onCellChanged}) { + VoidCallback? startListening({required void Function(T?) onCellChanged}) { if (isListening) { Log.error("Already started. It seems like you should call clone first"); return null; @@ -162,7 +163,7 @@ class _GridCellContext extends Equatable { } onCellChangedFn() { - onCellChanged(_cellDataNotifier.value as T); + onCellChanged(_cellDataNotifier.value); if (cellDataLoader.config.reloadOnCellChanged) { _loadData(); @@ -189,13 +190,26 @@ class _GridCellContext extends Equatable { return _fieldService.getFieldTypeOptionData(fieldType: fieldType); } - Future> saveCellData(D data) { - return cellDataPersistence.save(data); + void saveCellData(D data, {bool deduplicate = false, void Function(Option)? resultCallback}) async { + if (deduplicate) { + _loadDataOperation?.cancel(); + _loadDataOperation = Timer(const Duration(milliseconds: 300), () async { + final result = await cellDataPersistence.save(data); + if (resultCallback != null) { + resultCallback(result); + } + }); + } else { + final result = await cellDataPersistence.save(data); + if (resultCallback != null) { + resultCallback(result); + } + } } void _loadData() { - _delayOperation?.cancel(); - _delayOperation = Timer(const Duration(milliseconds: 10), () { + _loadDataOperation?.cancel(); + _loadDataOperation = Timer(const Duration(milliseconds: 10), () { cellDataLoader.loadData().then((data) { _cellDataNotifier.value = data; cellCache.insert(GridCellCacheData(key: _cacheKey, object: data)); @@ -204,7 +218,8 @@ class _GridCellContext extends Equatable { } void dispose() { - _delayOperation?.cancel(); + _loadDataOperation?.cancel(); + _saveDataOperation?.cancel(); if (_onFieldChangedFn != null) { cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart index 514ae2ce4e..b8e2b13bbc 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart @@ -58,7 +58,7 @@ class CheckboxCellBloc extends Bloc { class CheckboxCellEvent with _$CheckboxCellEvent { const factory CheckboxCellEvent.initial() = _Initial; const factory CheckboxCellEvent.select() = _Selected; - const factory CheckboxCellEvent.didReceiveCellUpdate(String cellData) = _DidReceiveCellUpdate; + const factory CheckboxCellEvent.didReceiveCellUpdate(String? cellData) = _DidReceiveCellUpdate; } @freezed 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 ff001eaa75..15f18707f8 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 @@ -37,7 +37,7 @@ class DateCalBloc extends Bloc { setFocusedDay: (focusedDay) { emit(state.copyWith(focusedDay: focusedDay)); }, - didReceiveCellUpdate: (DateCellData cellData) { + didReceiveCellUpdate: (DateCellData? cellData) { final dateData = dateDataFromCellData(cellData); final time = dateData.foldRight("", (dateData, previous) => dateData.time); emit(state.copyWith(dateData: dateData, time: time)); @@ -83,25 +83,26 @@ class DateCalBloc extends Bloc { return; } - final result = await cellContext.saveCellData(newDateData); - result.fold( - () => emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: none(), - )), - (err) { - switch (ErrorCode.valueOf(err.code)!) { - case ErrorCode.InvalidDateTimeFormat: - emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: Some(timeFormatPrompt(err)), - )); - break; - default: - Log.error(err); - } - }, - ); + cellContext.saveCellData(newDateData, resultCallback: (result) { + result.fold( + () => emit(state.copyWith( + dateData: Some(newDateData), + timeFormatError: none(), + )), + (err) { + switch (ErrorCode.valueOf(err.code)!) { + case ErrorCode.InvalidDateTimeFormat: + emit(state.copyWith( + dateData: Some(newDateData), + timeFormatError: Some(timeFormatPrompt(err)), + )); + break; + default: + Log.error(err); + } + }, + ); + }); } String timeFormatPrompt(FlowyError error) { @@ -183,7 +184,7 @@ class DateCalEvent with _$DateCalEvent { const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat; const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime; const factory DateCalEvent.setTime(String time) = _Time; - const factory DateCalEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate; + const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; } @freezed diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart index 4b068dd289..b06a3d60b3 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart @@ -16,7 +16,13 @@ class DateCellBloc extends Bloc { (event, emit) async { event.when( initial: () => _startListening(), - didReceiveCellUpdate: (DateCellData value) => emit(state.copyWith(data: Some(value))), + didReceiveCellUpdate: (DateCellData? cellData) { + if (cellData != null) { + emit(state.copyWith(data: Some(cellData))); + } else { + emit(state.copyWith(data: none())); + } + }, didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)), ); }, @@ -47,7 +53,7 @@ class DateCellBloc extends Bloc { @freezed class DateCellEvent with _$DateCellEvent { const factory DateCellEvent.initial() = _InitialCell; - const factory DateCellEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate; + const factory DateCellEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate; } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart index ceb89bc201..8157f6a3f2 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart @@ -19,7 +19,7 @@ class NumberCellBloc extends Bloc { _startListening(); }, didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith(content: value.cellContent)); + emit(state.copyWith(content: value.cellContent ?? "")); }, updateCell: (_UpdateCell value) async { await _updateCellValue(value, emit); @@ -58,7 +58,7 @@ class NumberCellBloc extends Bloc { class NumberCellEvent with _$NumberCellEvent { const factory NumberCellEvent.initial() = _Initial; const factory NumberCellEvent.updateCell(String text) = _UpdateCell; - const factory NumberCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate; + const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) = _DidReceiveCellUpdate; } @freezed diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart index 0b6b1fd4ab..c6393e4831 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_cell_bloc.dart @@ -44,7 +44,7 @@ class SelectOptionCellBloc extends Bloc { _onCellChangedFn = cellContext.startListening( onCellChanged: ((cellContent) { if (!isClosed) { - add(TextCellEvent.didReceiveCellUpdate(cellContent)); + add(TextCellEvent.didReceiveCellUpdate(cellContent ?? "")); } }), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart index 04284b16f7..609c625001 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart @@ -18,12 +18,11 @@ class URLCellBloc extends Bloc { initial: () { _startListening(); }, - updateText: (text) { - cellContext.saveCellData(text); - emit(state.copyWith(content: text)); - }, didReceiveCellUpdate: (cellData) { - emit(state.copyWith(content: cellData.content, url: cellData.url)); + emit(state.copyWith( + content: cellData?.content ?? "", + url: cellData?.url ?? "", + )); }, ); }, @@ -54,8 +53,7 @@ class URLCellBloc extends Bloc { @freezed class URLCellEvent with _$URLCellEvent { const factory URLCellEvent.initial() = _InitialCell; - const factory URLCellEvent.didReceiveCellUpdate(URLCellData cell) = _DidReceiveCellUpdate; - const factory URLCellEvent.updateText(String text) = _UpdateText; + const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate; } @freezed diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart new file mode 100644 index 0000000000..6e4990943f --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart @@ -0,0 +1,73 @@ +import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'dart:async'; +import 'cell_service/cell_service.dart'; + +part 'url_cell_editor_bloc.freezed.dart'; + +class URLCellEditorBloc extends Bloc { + final GridURLCellContext cellContext; + void Function()? _onCellChangedFn; + URLCellEditorBloc({ + required this.cellContext, + }) : super(URLCellEditorState.initial(cellContext)) { + on( + (event, emit) async { + event.when( + initial: () { + _startListening(); + }, + updateText: (text) { + cellContext.saveCellData(text, deduplicate: true); + emit(state.copyWith(content: text)); + }, + didReceiveCellUpdate: (cellData) { + emit(state.copyWith(content: cellData?.content ?? "")); + }, + ); + }, + ); + } + + @override + Future close() async { + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); + return super.close(); + } + + void _startListening() { + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((cellData) { + if (!isClosed) { + add(URLCellEditorEvent.didReceiveCellUpdate(cellData)); + } + }), + ); + } +} + +@freezed +class URLCellEditorEvent with _$URLCellEditorEvent { + const factory URLCellEditorEvent.initial() = _InitialCell; + const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate; + const factory URLCellEditorEvent.updateText(String text) = _UpdateText; +} + +@freezed +class URLCellEditorState with _$URLCellEditorState { + const factory URLCellEditorState({ + required String content, + }) = _URLCellEditorState; + + factory URLCellEditorState.initial(GridURLCellContext context) { + final cellData = context.getCellData(); + return URLCellEditorState( + content: cellData?.content ?? "", + ); + } +} 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 8ae79541a3..f8189e7f02 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 @@ -13,7 +13,7 @@ import 'date_cell/date_cell.dart'; import 'number_cell.dart'; import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; -import 'url_cell.dart'; +import 'url_cell/url_cell.dart'; GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) { final key = ValueKey(gridCell.cellId()); @@ -35,7 +35,6 @@ GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, { return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key); case FieldType.URL: return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key); - } throw UnimplementedError; } @@ -151,7 +150,7 @@ class CellContainer extends StatelessWidget { }); if (expander != null) { - container = _CellEnterRegion(child: container, expander: expander!); + container = CellEnterRegion(child: container, expander: expander!); } return GestureDetector( @@ -181,10 +180,10 @@ class CellContainer extends StatelessWidget { } } -class _CellEnterRegion extends StatelessWidget { +class CellEnterRegion extends StatelessWidget { final Widget child; final Widget expander; - const _CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key); + const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart deleted file mode 100644 index 328d0e7180..0000000000 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'dart:async'; -import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:app_flowy/workspace/application/grid/prelude.dart'; -import 'cell_builder.dart'; - -class GridURLCellStyle extends GridCellStyle { - String? placeholder; - - GridURLCellStyle({ - this.placeholder, - }); -} - -class GridURLCell extends StatefulWidget with GridCellWidget { - final GridCellContextBuilder cellContextBuilder; - late final GridURLCellStyle? cellStyle; - GridURLCell({ - required this.cellContextBuilder, - GridCellStyle? style, - Key? key, - }) : super(key: key) { - if (style != null) { - cellStyle = (style as GridURLCellStyle); - } else { - cellStyle = null; - } - } - - @override - State createState() => _GridURLCellState(); -} - -class _GridURLCellState extends State { - late URLCellBloc _cellBloc; - late TextEditingController _controller; - late CellSingleFocusNode _focusNode; - Timer? _delayOperation; - - @override - void initState() { - final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; - _cellBloc = URLCellBloc(cellContext: cellContext); - _cellBloc.add(const URLCellEvent.initial()); - _controller = TextEditingController(text: _cellBloc.state.content); - _focusNode = CellSingleFocusNode(); - - _listenFocusNode(); - _listenRequestFocus(context); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider.value( - value: _cellBloc, - child: BlocListener( - listener: (context, state) { - if (_controller.text != state.content) { - _controller.text = state.content; - } - }, - child: TextField( - controller: _controller, - focusNode: _focusNode, - onChanged: (value) => focusChanged(), - onEditingComplete: () => _focusNode.unfocus(), - maxLines: null, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), - decoration: InputDecoration( - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - hintText: widget.cellStyle?.placeholder, - isDense: true, - ), - ), - ), - ); - } - - @override - Future dispose() async { - widget.requestFocus.removeAllListener(); - _delayOperation?.cancel(); - _cellBloc.close(); - _focusNode.removeSingleListener(); - _focusNode.dispose(); - - super.dispose(); - } - - @override - void didUpdateWidget(covariant GridURLCell oldWidget) { - if (oldWidget != widget) { - _listenFocusNode(); - } - super.didUpdateWidget(oldWidget); - } - - void _listenFocusNode() { - widget.onFocus.value = _focusNode.hasFocus; - _focusNode.setSingleListener(() { - widget.onFocus.value = _focusNode.hasFocus; - focusChanged(); - }); - } - - void _listenRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { - if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { - FocusScope.of(context).requestFocus(_focusNode); - } - }); - } - - Future focusChanged() async { - if (mounted) { - _delayOperation?.cancel(); - _delayOperation = Timer(const Duration(milliseconds: 300), () { - if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { - _cellBloc.add(URLCellEvent.updateText(_controller.text)); - } - }); - } - } -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart new file mode 100644 index 0000000000..055a4947c8 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart @@ -0,0 +1,96 @@ +import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/workspace/application/grid/cell/url_cell_editor_bloc.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter_bloc/flutter_bloc.dart'; + +class URLCellEditor extends StatefulWidget { + final GridURLCellContext cellContext; + const URLCellEditor({required this.cellContext, Key? key}) : super(key: key); + + @override + State createState() => _URLCellEditorState(); + + static void show( + BuildContext context, + GridURLCellContext cellContext, + ) { + FlowyOverlay.of(context).remove(identifier()); + final editor = URLCellEditor( + cellContext: cellContext, + ); + + // + FlowyOverlay.of(context).insertWithAnchor( + widget: OverlayContainer( + child: SizedBox(width: 200, child: editor), + constraints: BoxConstraints.loose(const Size(300, 160)), + ), + identifier: URLCellEditor.identifier(), + anchorContext: context, + anchorDirection: AnchorDirection.bottomWithCenterAligned, + ); + } + + static String identifier() { + return (URLCellEditor).toString(); + } +} + +class _URLCellEditorState extends State { + late URLCellEditorBloc _cellBloc; + late TextEditingController _controller; + + @override + void initState() { + _cellBloc = URLCellEditorBloc(cellContext: widget.cellContext); + _cellBloc.add(const URLCellEditorEvent.initial()); + _controller = TextEditingController(text: _cellBloc.state.content); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider.value( + value: _cellBloc, + child: BlocListener( + listener: (context, state) { + if (_controller.text != state.content) { + _controller.text = state.content; + } + }, + child: TextField( + autofocus: true, + controller: _controller, + onChanged: (value) => focusChanged(), + maxLines: null, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + hintText: "", + isDense: true, + ), + ), + ), + ); + } + + @override + Future dispose() async { + _cellBloc.close(); + + super.dispose(); + } + + Future focusChanged() async { + if (mounted) { + if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { + _cellBloc.add(URLCellEditorEvent.updateText(_controller.text)); + } + } + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart new file mode 100644 index 0000000000..db0dcade79 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -0,0 +1,131 @@ +import 'dart:async'; +import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/prelude.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../cell_builder.dart'; +import 'cell_editor.dart'; + +class GridURLCellStyle extends GridCellStyle { + String? placeholder; + + GridURLCellStyle({ + this.placeholder, + }); +} + +class GridURLCell extends StatefulWidget with GridCellWidget { + final GridCellContextBuilder cellContextBuilder; + late final GridURLCellStyle? cellStyle; + GridURLCell({ + required this.cellContextBuilder, + GridCellStyle? style, + Key? key, + }) : super(key: key) { + if (style != null) { + cellStyle = (style as GridURLCellStyle); + } else { + cellStyle = null; + } + } + + @override + State createState() => _GridURLCellState(); +} + +class _GridURLCellState extends State { + late URLCellBloc _cellBloc; + + @override + void initState() { + final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; + _cellBloc = URLCellBloc(cellContext: cellContext); + _cellBloc.add(const URLCellEvent.initial()); + _listenRequestFocus(context); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return BlocProvider.value( + value: _cellBloc, + child: BlocBuilder( + builder: (context, state) { + final richText = RichText( + textAlign: TextAlign.left, + text: TextSpan( + text: state.content, + style: TextStyle( + color: theme.main2, + fontSize: 14, + decoration: TextDecoration.underline, + ), + recognizer: _tapGesture(context), + ), + ); + + return CellEnterRegion( + child: Align(alignment: Alignment.centerLeft, child: richText), + expander: _EditCellIndicator(onTap: () {}), + ); + }, + ), + ); + } + + @override + Future dispose() async { + widget.requestFocus.removeAllListener(); + _cellBloc.close(); + super.dispose(); + } + + TapGestureRecognizer _tapGesture(BuildContext context) { + final gesture = TapGestureRecognizer(); + gesture.onTap = () async { + final url = context.read().state.url; + await _openUrlOrEdit(url); + }; + return gesture; + } + + Future _openUrlOrEdit(String url) async { + final uri = Uri.parse(url); + if (url.isNotEmpty && await canLaunchUrl(uri)) { + await launchUrl(uri); + } else { + final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; + URLCellEditor.show(context, cellContext); + } + } + + void _listenRequestFocus(BuildContext context) { + widget.requestFocus.addListener(() { + _openUrlOrEdit(_cellBloc.state.url); + }); + } +} + +class _EditCellIndicator extends StatelessWidget { + final VoidCallback onTap; + const _EditCellIndicator({required this.onTap, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return FlowyIconButton( + width: 26, + onPressed: onTap, + hoverColor: theme.hover, + radius: BorderRadius.circular(4), + iconPadding: const EdgeInsets.all(5), + icon: svgWidget("editor/edit", color: theme.iconColor), + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index 200a079d55..a643b58928 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -209,9 +209,10 @@ class _CellExpander extends StatelessWidget { return FittedBox( fit: BoxFit.contain, child: FlowyIconButton( + width: 26, onPressed: onExpand, - iconPadding: const EdgeInsets.fromLTRB(6, 6, 6, 6), - fillColor: theme.surface, + iconPadding: const EdgeInsets.all(5), + radius: BorderRadius.circular(4), icon: svgWidget("grid/expander", color: theme.main1), ), ); 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 255553f3d2..0900039b1f 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 @@ -4,7 +4,7 @@ import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart'; import 'package:flowy_infra/image.dart'; diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 59ca0e3dec..393a02ac73 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -954,6 +954,7 @@ dependencies = [ "strum_macros", "tokio", "tracing", + "url", ] [[package]] diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index a4677d727f..43b0cbf69f 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -36,6 +36,7 @@ serde_json = {version = "1.0"} serde_repr = "0.1" indexmap = {version = "1.8.1", features = ["serde"]} fancy-regex = "0.10.0" +url = { version = "2"} [dev-dependencies] flowy-test = { path = "../flowy-test" } 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 26efd34acc..81a7ff5c04 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 @@ -207,8 +207,6 @@ impl CellDataOperation for MultiSelectTypeOption { return Ok(DecodedCellData::default()); } - tracing::info!("😁{}", self.options.len()); - let encoded_data = encoded_data.into(); let select_options = select_option_ids(encoded_data) .into_iter() diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 6cef992fbd..7299b1babd 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -62,7 +62,19 @@ impl CellDataOperation, String> for URLTypeOption { }; if let Ok(Some(m)) = URL_REGEX.find(&changeset) { - cell_data.url = m.as_str().to_string(); + // Only support https scheme by now + match url::Url::parse(m.as_str()) { + Ok(url) => { + if url.scheme() == "https" { + cell_data.url = url.into(); + } else { + cell_data.url = format!("https://{}", m.as_str()); + } + } + Err(_) => { + cell_data.url = format!("https://{}", m.as_str()); + } + } } cell_data.to_json() @@ -132,7 +144,7 @@ mod tests { &field_type, &field_meta, "AppFlowy website - https://www.appflowy.io", - "https://www.appflowy.io", + "https://www.appflowy.io/", ); assert_changeset( @@ -141,7 +153,7 @@ mod tests { &field_type, &field_meta, "AppFlowy website appflowy.io", - "appflowy.io", + "https://appflowy.io", ); } 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 3191fa2cc0..d93f84244a 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 @@ -167,7 +167,6 @@ pub fn decode_cell_data>( field_meta: &FieldMeta, ) -> FlowyResult { let encoded_data = encoded_data.into(); - tracing::info!("😁{:?}", field_meta.type_options); let get_cell_data = || { let data = match t_field_type { FieldType::RichText => field_meta From c2b970bdd653637b03f1efb41f84911b27c68cfb Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Sun, 29 May 2022 16:50:36 +0200 Subject: [PATCH 34/82] fix: configured to use AppFlowy Directory --- .../workspace/application/doc/share_bloc.dart | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index cea6f724f9..5177c93535 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; @@ -40,8 +41,21 @@ class DocShareBloc extends Bloc { return value; } + Future appFlowyDocumentDirectory() async { + Directory documentsDir = await getApplicationDocumentsDirectory(); + + switch (integrationEnv()) { + case IntegrationMode.develop: + return Directory('${documentsDir.path}/flowy_dev').create(); + case IntegrationMode.release: + return Directory('${documentsDir.path}/flowy').create(); + case IntegrationMode.test: + return Directory("${Directory.current.path}/.sandbox"); + } + } + Future get _localPath async { - final dir = await getApplicationDocumentsDirectory(); + final dir = await appFlowyDocumentDirectory(); return dir.path; } From bfa3eb9577c4ce3b6b64ff9a4bead47b3ac33f0f Mon Sep 17 00:00:00 2001 From: Poly-Pixel <79737178+Poly-Pixel@users.noreply.github.com> Date: Sun, 29 May 2022 15:15:57 -0400 Subject: [PATCH 35/82] fix: Fix various small issues --- .../presentation/home/menu/app/header/add_button.dart | 2 +- .../plugins/doc/src/widget/toolbar/toolbar_icon_button.dart | 4 ++-- frontend/app_flowy/packages/flowy_infra/lib/text_style.dart | 2 +- .../flowy_infra_ui/lib/widget/buttons/primary_button.dart | 2 +- .../flowy_infra_ui/lib/widget/buttons/secondary_button.dart | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart index a7de233006..a015930a3e 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart @@ -28,7 +28,7 @@ class AddButton extends StatelessWidget { onSelected: onSelected, ).show(context); }, - icon: svgWidget("home/add").padding(horizontal: 3, vertical: 3), + icon: svgWidget("home/add", color: theme.iconColor).padding(horizontal: 3, vertical: 3), ); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/toolbar_icon_button.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/toolbar_icon_button.dart index b6dddefbb3..aac5b5a4b1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/toolbar_icon_button.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/src/widget/toolbar/toolbar_icon_button.dart @@ -29,9 +29,9 @@ class ToolbarIconButton extends StatelessWidget { iconPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), onPressed: onPressed, width: width, - icon: isToggled == true ? svgWidget(iconName, color: Colors.white) : svgWidget(iconName), + icon: isToggled == true ? svgWidget(iconName, color: Colors.white) : svgWidget(iconName, color: theme.iconColor), fillColor: isToggled == true ? theme.main1 : theme.shader6, - hoverColor: isToggled == true ? theme.main1 : theme.shader5, + hoverColor: isToggled == true ? theme.main1 : theme.hover, tooltipText: tooltipText, ); } diff --git a/frontend/app_flowy/packages/flowy_infra/lib/text_style.dart b/frontend/app_flowy/packages/flowy_infra/lib/text_style.dart index 26619834bc..9187e17f6e 100644 --- a/frontend/app_flowy/packages/flowy_infra/lib/text_style.dart +++ b/frontend/app_flowy/packages/flowy_infra/lib/text_style.dart @@ -71,7 +71,7 @@ class TextStyles { static TextStyle get CalloutFocus => Callout.bold; // ignore: non_constant_identifier_names - static TextStyle get Btn => quicksand.bold.size(FontSizes.s14).letterSpace(1.75); + static TextStyle get Btn => quicksand.bold.size(FontSizes.s16).letterSpace(1.75); // ignore: non_constant_identifier_names static TextStyle get BtnSelected => quicksand.size(FontSizes.s14).letterSpace(1.75); diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart index 52b2624385..3cd1cdf81e 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart @@ -15,7 +15,7 @@ class PrimaryTextButton extends StatelessWidget { @override Widget build(BuildContext context) { - TextStyle txtStyle = TextStyles.Footnote.textColor(Colors.white); + TextStyle txtStyle = TextStyles.Btn.textColor(Colors.white); return PrimaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle)); } } diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart index 77c89fa13e..dedef61295 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart @@ -17,7 +17,7 @@ class SecondaryTextButton extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); - TextStyle txtStyle = TextStyles.Footnote.textColor(theme.main1); + TextStyle txtStyle = TextStyles.Btn.textColor(theme.main1); return SecondaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle)); } } From 41f2ad09dd914601a38831782b9d5627492c74b1 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Mon, 30 May 2022 00:07:26 +0200 Subject: [PATCH 36/82] feat: testing overlay notification --- .../workspace/application/doc/share_bloc.dart | 8 ++++++ .../presentation/widgets/dialogs.dart | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index 5177c93535..f5be9f37af 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; +import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; @@ -34,6 +35,8 @@ class DocShareBloc extends Bloc { }); } + bool checkFile = false; + ExportData _convertDeltaToMarkdown(ExportData value) { final result = deltaToMarkdown(value.data); value.data = result; @@ -61,11 +64,16 @@ class DocShareBloc extends Bloc { Future get _localFile async { final path = await _localPath; + checkFile = true; return File('$path/${view.name}.md'); } Future writeFile(String md) async { final file = await _localFile; + if (checkFile) + BubbleNotification(msgTitle: 'Export To Markdown', msgBody: 'File saved to $file'); + else + BubbleNotification(msgTitle: 'Failed to write to file', msgBody: '$file'); return file.writeAsString(md); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index 3a5c1c79e5..fda8d37d9c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -14,6 +14,7 @@ import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart'; import 'package:textstyle_extensions/textstyle_extensions.dart'; export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:overlay_support/overlay_support.dart'; class TextFieldDialog extends StatefulWidget { final String value; @@ -219,3 +220,27 @@ class OkCancelButton extends StatelessWidget { ); } } + +class BubbleNotification extends StatelessWidget { + final String msgTitle; + final String msgBody; + + const BubbleNotification({Key? key, required this.msgTitle, required this.msgBody}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.symmetric(horizontal: 4), + child: SafeArea( + child: ListTile( + leading: SizedBox.fromSize( + size: const Size(40, 40), + // child: ClipOval(child: ) + ), + title: Text(msgTitle), + subtitle: Text(msgBody), + ), + // title: Text('Action') + )); + } +} From 5da8144456ae60e3711f394f0497ec7a919e5cad Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 30 May 2022 08:56:08 +0800 Subject: [PATCH 37/82] chore: show url cell editor when tap on edit button --- .../plugins/grid/src/widgets/cell/url_cell/url_cell.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index db0dcade79..917cee66d2 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -72,7 +72,10 @@ class _GridURLCellState extends State { return CellEnterRegion( child: Align(alignment: Alignment.centerLeft, child: richText), - expander: _EditCellIndicator(onTap: () {}), + expander: _EditCellIndicator(onTap: () { + final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; + URLCellEditor.show(context, cellContext); + }), ); }, ), From 546b1d22f4370721883b81002f31ecdee2f735ff Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 30 May 2022 10:42:30 +0800 Subject: [PATCH 38/82] fix: #509 enable select on tag --- .../cell/select_option_cell/extension.dart | 28 ++++++++++--------- .../select_option_editor.dart | 6 +++- 2 files changed, 20 insertions(+), 14 deletions(-) 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 6f8212106a..24817087c7 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 @@ -1,6 +1,7 @@ import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -64,9 +65,11 @@ class SelectOptionTag extends StatelessWidget { final String name; final Color color; final bool isSelected; + final VoidCallback? onSelected; const SelectOptionTag({ required this.name, required this.color, + this.onSelected, this.isSelected = false, Key? key, }) : super(key: key); @@ -74,12 +77,14 @@ class SelectOptionTag extends StatelessWidget { factory SelectOptionTag.fromSelectOption({ required BuildContext context, required SelectOption option, + VoidCallback? onSelected, bool isSelected = false, }) { return SelectOptionTag( name: option.name, color: option.color.make(context), isSelected: isSelected, + onSelected: onSelected, ); } @@ -92,19 +97,12 @@ class SelectOptionTag extends StatelessWidget { backgroundColor: color, labelPadding: const EdgeInsets.symmetric(horizontal: 6), selected: true, - onSelected: (_) {}, + onSelected: (_) { + if (onSelected != null) { + onSelected!(); + } + }, ); - - // return Container( - // decoration: BoxDecoration( - // color: option.color.make(context), - // shape: BoxShape.rectangle, - // borderRadius: BorderRadius.circular(8.0), - // ), - // child: Center(child: FlowyText.medium(option.name, fontSize: 12)), - // margin: const EdgeInsets.symmetric(horizontal: 3.0), - // padding: const EdgeInsets.symmetric(horizontal: 6.0), - // ); } } @@ -136,7 +134,11 @@ class SelectOptionTagCell extends StatelessWidget { Flexible( fit: FlexFit.loose, flex: 2, - child: SelectOptionTag.fromSelectOption(context: context, option: option), + child: SelectOptionTag.fromSelectOption( + context: context, + option: option, + onSelected: () => onSelected(option), + ), ), const Spacer(), ...children, 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 01972eb41a..1829157f51 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 @@ -233,7 +233,11 @@ class _SelectOptionCell extends StatelessWidget { context.read().add(SelectOptionEditorEvent.selectOption(option.id)); }, children: [ - if (isSelected) svgWidget("grid/checkmark"), + if (isSelected) + Padding( + padding: const EdgeInsets.only(right: 6), + child: svgWidget("grid/checkmark"), + ), ], ), ), From 84454469b425265c6563d016281c7073f3453496 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 30 May 2022 18:40:42 +0800 Subject: [PATCH 39/82] fix: #508 auto format number --- .../grid/cell/number_cell_bloc.dart | 19 +- .../grid/field/type_option/number_bloc.dart | 1 + .../field/type_option/number_format_bloc.dart | 2 +- .../grid/src/widgets/cell/number_cell.dart | 7 +- .../cell/select_option_cell/extension.dart | 1 - .../widgets/header/type_option/number.dart | 2 +- .../lib/protobuf/flowy-grid/format.pb.dart | 11 + .../protobuf/flowy-grid/format.pbenum.dart | 94 +++++ .../protobuf/flowy-grid/format.pbjson.dart | 55 +++ .../protobuf/flowy-grid/format.pbserver.dart | 9 + .../flowy-grid/number_type_option.pb.dart | 12 +- .../flowy-grid/number_type_option.pbenum.dart | 87 ----- .../flowy-grid/number_type_option.pbjson.dart | 45 --- .../lib/protobuf/flowy-grid/protobuf.dart | 1 + .../flowy-grid/src/protobuf/model/format.rs | 207 +++++++++++ .../flowy-grid/src/protobuf/model/mod.rs | 3 + .../src/protobuf/model/number_type_option.rs | 194 +--------- .../src/protobuf/proto/format.proto | 40 ++ .../protobuf/proto/number_type_option.proto | 39 +- .../format.rs} | 312 +--------------- .../type_options/number_type_option/mod.rs | 6 + .../number_type_option/number_type_option.rs | 348 ++++++++++++++++++ 22 files changed, 813 insertions(+), 682 deletions(-) create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pb.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbenum.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbjson.dart create mode 100644 frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbserver.dart create mode 100644 frontend/rust-lib/flowy-grid/src/protobuf/model/format.rs create mode 100644 frontend/rust-lib/flowy-grid/src/protobuf/proto/format.proto rename frontend/rust-lib/flowy-grid/src/services/field/type_options/{number_type_option.rs => number_type_option/format.rs} (54%) create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart index 8157f6a3f2..b694ac5059 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart @@ -1,3 +1,4 @@ +import 'package:flowy_sdk/log.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -14,25 +15,23 @@ class NumberCellBloc extends Bloc { }) : super(NumberCellState.initial(cellContext)) { on( (event, emit) async { - await event.map( - initial: (_Initial value) async { + await event.when( + initial: () async { _startListening(); }, - didReceiveCellUpdate: (_DidReceiveCellUpdate value) { - emit(state.copyWith(content: value.cellContent ?? "")); + didReceiveCellUpdate: (cellContent) { + emit(state.copyWith(content: cellContent ?? "")); }, - updateCell: (_UpdateCell value) async { - await _updateCellValue(value, emit); + updateCell: (text) async { + cellContext.saveCellData(text, resultCallback: (result) { + result.fold(() => null, (err) => Log.error(err)); + }); }, ); }, ); } - Future _updateCellValue(_UpdateCell value, Emitter emit) async { - cellContext.saveCellData(value.text); - } - @override Future close() async { if (_onCellChangedFn != null) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart index 951bdb9261..a708668066 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_bloc.dart @@ -1,4 +1,5 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_format_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_format_bloc.dart index 74f1531ce3..a0a853913c 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_format_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/number_format_bloc.dart @@ -1,4 +1,4 @@ -import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; 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 dc694f48f9..da0b222d25 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 @@ -87,12 +87,7 @@ class _NumberCellState extends State { _delayOperation?.cancel(); _delayOperation = Timer(const Duration(milliseconds: 300), () { if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { - final number = num.tryParse(_controller.text); - if (number != null) { - _cellBloc.add(NumberCellEvent.updateCell(_controller.text)); - } else { - _controller.text = ""; - } + _cellBloc.add(NumberCellEvent.updateCell(_controller.text)); } }); } 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 24817087c7..27e36ad46d 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 @@ -1,7 +1,6 @@ import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart index 3a330a02b7..bca17e1cd8 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart @@ -9,7 +9,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:easy_localization/easy_localization.dart' hide NumberFormat; diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pb.dart new file mode 100644 index 0000000000..6983d0e025 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pb.dart @@ -0,0 +1,11 @@ +/// +// Generated code. Do not modify. +// source: format.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + +import 'dart:core' as $core; + +export 'format.pbenum.dart'; + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbenum.dart new file mode 100644 index 0000000000..73c544f8a5 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbenum.dart @@ -0,0 +1,94 @@ +/// +// Generated code. Do not modify. +// source: format.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields + +// ignore_for_file: UNDEFINED_SHOWN_NAME +import 'dart:core' as $core; +import 'package:protobuf/protobuf.dart' as $pb; + +class NumberFormat extends $pb.ProtobufEnum { + static const NumberFormat Number = NumberFormat._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number'); + static const NumberFormat USD = NumberFormat._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'USD'); + static const NumberFormat CanadianDollar = NumberFormat._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CanadianDollar'); + static const NumberFormat EUR = NumberFormat._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EUR'); + static const NumberFormat Pound = NumberFormat._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Pound'); + static const NumberFormat Yen = NumberFormat._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Yen'); + static const NumberFormat Ruble = NumberFormat._(7, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ruble'); + static const NumberFormat Rupee = NumberFormat._(8, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rupee'); + static const NumberFormat Won = NumberFormat._(9, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Won'); + static const NumberFormat Yuan = NumberFormat._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Yuan'); + static const NumberFormat Real = NumberFormat._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Real'); + static const NumberFormat Lira = NumberFormat._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Lira'); + static const NumberFormat Rupiah = NumberFormat._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rupiah'); + static const NumberFormat Franc = NumberFormat._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Franc'); + static const NumberFormat HongKongDollar = NumberFormat._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'HongKongDollar'); + static const NumberFormat NewZealandDollar = NumberFormat._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewZealandDollar'); + static const NumberFormat Krona = NumberFormat._(17, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Krona'); + static const NumberFormat NorwegianKrone = NumberFormat._(18, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NorwegianKrone'); + static const NumberFormat MexicanPeso = NumberFormat._(19, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MexicanPeso'); + static const NumberFormat Rand = NumberFormat._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rand'); + static const NumberFormat NewTaiwanDollar = NumberFormat._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewTaiwanDollar'); + static const NumberFormat DanishKrone = NumberFormat._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DanishKrone'); + static const NumberFormat Baht = NumberFormat._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Baht'); + static const NumberFormat Forint = NumberFormat._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Forint'); + static const NumberFormat Koruna = NumberFormat._(25, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Koruna'); + static const NumberFormat Shekel = NumberFormat._(26, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Shekel'); + static const NumberFormat ChileanPeso = NumberFormat._(27, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ChileanPeso'); + static const NumberFormat PhilippinePeso = NumberFormat._(28, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PhilippinePeso'); + static const NumberFormat Dirham = NumberFormat._(29, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Dirham'); + static const NumberFormat ColombianPeso = NumberFormat._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ColombianPeso'); + static const NumberFormat Riyal = NumberFormat._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Riyal'); + static const NumberFormat Ringgit = NumberFormat._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ringgit'); + static const NumberFormat Leu = NumberFormat._(33, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Leu'); + static const NumberFormat ArgentinePeso = NumberFormat._(34, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ArgentinePeso'); + static const NumberFormat UruguayanPeso = NumberFormat._(35, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UruguayanPeso'); + static const NumberFormat Percent = NumberFormat._(36, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Percent'); + + static const $core.List values = [ + Number, + USD, + CanadianDollar, + EUR, + Pound, + Yen, + Ruble, + Rupee, + Won, + Yuan, + Real, + Lira, + Rupiah, + Franc, + HongKongDollar, + NewZealandDollar, + Krona, + NorwegianKrone, + MexicanPeso, + Rand, + NewTaiwanDollar, + DanishKrone, + Baht, + Forint, + Koruna, + Shekel, + ChileanPeso, + PhilippinePeso, + Dirham, + ColombianPeso, + Riyal, + Ringgit, + Leu, + ArgentinePeso, + UruguayanPeso, + Percent, + ]; + + static final $core.Map<$core.int, NumberFormat> _byValue = $pb.ProtobufEnum.initByValue(values); + static NumberFormat? valueOf($core.int value) => _byValue[value]; + + const NumberFormat._($core.int v, $core.String n) : super(v, n); +} + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbjson.dart new file mode 100644 index 0000000000..69071ef303 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbjson.dart @@ -0,0 +1,55 @@ +/// +// Generated code. Do not modify. +// source: format.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +import 'dart:core' as $core; +import 'dart:convert' as $convert; +import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use numberFormatDescriptor instead') +const NumberFormat$json = const { + '1': 'NumberFormat', + '2': const [ + const {'1': 'Number', '2': 0}, + const {'1': 'USD', '2': 1}, + const {'1': 'CanadianDollar', '2': 2}, + const {'1': 'EUR', '2': 4}, + const {'1': 'Pound', '2': 5}, + const {'1': 'Yen', '2': 6}, + const {'1': 'Ruble', '2': 7}, + const {'1': 'Rupee', '2': 8}, + const {'1': 'Won', '2': 9}, + const {'1': 'Yuan', '2': 10}, + const {'1': 'Real', '2': 11}, + const {'1': 'Lira', '2': 12}, + const {'1': 'Rupiah', '2': 13}, + const {'1': 'Franc', '2': 14}, + const {'1': 'HongKongDollar', '2': 15}, + const {'1': 'NewZealandDollar', '2': 16}, + const {'1': 'Krona', '2': 17}, + const {'1': 'NorwegianKrone', '2': 18}, + const {'1': 'MexicanPeso', '2': 19}, + const {'1': 'Rand', '2': 20}, + const {'1': 'NewTaiwanDollar', '2': 21}, + const {'1': 'DanishKrone', '2': 22}, + const {'1': 'Baht', '2': 23}, + const {'1': 'Forint', '2': 24}, + const {'1': 'Koruna', '2': 25}, + const {'1': 'Shekel', '2': 26}, + const {'1': 'ChileanPeso', '2': 27}, + const {'1': 'PhilippinePeso', '2': 28}, + const {'1': 'Dirham', '2': 29}, + const {'1': 'ColombianPeso', '2': 30}, + const {'1': 'Riyal', '2': 31}, + const {'1': 'Ringgit', '2': 32}, + const {'1': 'Leu', '2': 33}, + const {'1': 'ArgentinePeso', '2': 34}, + const {'1': 'UruguayanPeso', '2': 35}, + const {'1': 'Percent', '2': 36}, + ], +}; + +/// Descriptor for `NumberFormat`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List numberFormatDescriptor = $convert.base64Decode('CgxOdW1iZXJGb3JtYXQSCgoGTnVtYmVyEAASBwoDVVNEEAESEgoOQ2FuYWRpYW5Eb2xsYXIQAhIHCgNFVVIQBBIJCgVQb3VuZBAFEgcKA1llbhAGEgkKBVJ1YmxlEAcSCQoFUnVwZWUQCBIHCgNXb24QCRIICgRZdWFuEAoSCAoEUmVhbBALEggKBExpcmEQDBIKCgZSdXBpYWgQDRIJCgVGcmFuYxAOEhIKDkhvbmdLb25nRG9sbGFyEA8SFAoQTmV3WmVhbGFuZERvbGxhchAQEgkKBUtyb25hEBESEgoOTm9yd2VnaWFuS3JvbmUQEhIPCgtNZXhpY2FuUGVzbxATEggKBFJhbmQQFBITCg9OZXdUYWl3YW5Eb2xsYXIQFRIPCgtEYW5pc2hLcm9uZRAWEggKBEJhaHQQFxIKCgZGb3JpbnQQGBIKCgZLb3J1bmEQGRIKCgZTaGVrZWwQGhIPCgtDaGlsZWFuUGVzbxAbEhIKDlBoaWxpcHBpbmVQZXNvEBwSCgoGRGlyaGFtEB0SEQoNQ29sb21iaWFuUGVzbxAeEgkKBVJpeWFsEB8SCwoHUmluZ2dpdBAgEgcKA0xldRAhEhEKDUFyZ2VudGluZVBlc28QIhIRCg1VcnVndWF5YW5QZXNvECMSCwoHUGVyY2VudBAk'); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbserver.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbserver.dart new file mode 100644 index 0000000000..f61c8f2533 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/format.pbserver.dart @@ -0,0 +1,9 @@ +/// +// Generated code. Do not modify. +// source: format.proto +// +// @dart = 2.12 +// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package + +export 'format.pb.dart'; + diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pb.dart index 54f4d9546f..83ded2177b 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pb.dart @@ -9,13 +9,11 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -import 'number_type_option.pbenum.dart'; - -export 'number_type_option.pbenum.dart'; +import 'format.pbenum.dart' as $0; class NumberTypeOption extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NumberTypeOption', createEmptyInstance: create) - ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format', $pb.PbFieldType.OE, defaultOrMaker: NumberFormat.Number, valueOf: NumberFormat.valueOf, enumValues: NumberFormat.values) + ..e<$0.NumberFormat>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'format', $pb.PbFieldType.OE, defaultOrMaker: $0.NumberFormat.Number, valueOf: $0.NumberFormat.valueOf, enumValues: $0.NumberFormat.values) ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'scale', $pb.PbFieldType.OU3) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'symbol') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'signPositive') @@ -25,7 +23,7 @@ class NumberTypeOption extends $pb.GeneratedMessage { NumberTypeOption._() : super(); factory NumberTypeOption({ - NumberFormat? format, + $0.NumberFormat? format, $core.int? scale, $core.String? symbol, $core.bool? signPositive, @@ -71,9 +69,9 @@ class NumberTypeOption extends $pb.GeneratedMessage { static NumberTypeOption? _defaultInstance; @$pb.TagNumber(1) - NumberFormat get format => $_getN(0); + $0.NumberFormat get format => $_getN(0); @$pb.TagNumber(1) - set format(NumberFormat v) { setField(1, v); } + set format($0.NumberFormat v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasFormat() => $_has(0); @$pb.TagNumber(1) diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbenum.dart index baa7e2f043..06034a2f8b 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbenum.dart @@ -5,90 +5,3 @@ // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields -// ignore_for_file: UNDEFINED_SHOWN_NAME -import 'dart:core' as $core; -import 'package:protobuf/protobuf.dart' as $pb; - -class NumberFormat extends $pb.ProtobufEnum { - static const NumberFormat Number = NumberFormat._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number'); - static const NumberFormat USD = NumberFormat._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'USD'); - static const NumberFormat CanadianDollar = NumberFormat._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CanadianDollar'); - static const NumberFormat EUR = NumberFormat._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EUR'); - static const NumberFormat Pound = NumberFormat._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Pound'); - static const NumberFormat Yen = NumberFormat._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Yen'); - static const NumberFormat Ruble = NumberFormat._(7, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ruble'); - static const NumberFormat Rupee = NumberFormat._(8, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rupee'); - static const NumberFormat Won = NumberFormat._(9, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Won'); - static const NumberFormat Yuan = NumberFormat._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Yuan'); - static const NumberFormat Real = NumberFormat._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Real'); - static const NumberFormat Lira = NumberFormat._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Lira'); - static const NumberFormat Rupiah = NumberFormat._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rupiah'); - static const NumberFormat Franc = NumberFormat._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Franc'); - static const NumberFormat HongKongDollar = NumberFormat._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'HongKongDollar'); - static const NumberFormat NewZealandDollar = NumberFormat._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewZealandDollar'); - static const NumberFormat Krona = NumberFormat._(17, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Krona'); - static const NumberFormat NorwegianKrone = NumberFormat._(18, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NorwegianKrone'); - static const NumberFormat MexicanPeso = NumberFormat._(19, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MexicanPeso'); - static const NumberFormat Rand = NumberFormat._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Rand'); - static const NumberFormat NewTaiwanDollar = NumberFormat._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewTaiwanDollar'); - static const NumberFormat DanishKrone = NumberFormat._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DanishKrone'); - static const NumberFormat Baht = NumberFormat._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Baht'); - static const NumberFormat Forint = NumberFormat._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Forint'); - static const NumberFormat Koruna = NumberFormat._(25, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Koruna'); - static const NumberFormat Shekel = NumberFormat._(26, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Shekel'); - static const NumberFormat ChileanPeso = NumberFormat._(27, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ChileanPeso'); - static const NumberFormat PhilippinePeso = NumberFormat._(28, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PhilippinePeso'); - static const NumberFormat Dirham = NumberFormat._(29, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Dirham'); - static const NumberFormat ColombianPeso = NumberFormat._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ColombianPeso'); - static const NumberFormat Riyal = NumberFormat._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Riyal'); - static const NumberFormat Ringgit = NumberFormat._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ringgit'); - static const NumberFormat Leu = NumberFormat._(33, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Leu'); - static const NumberFormat ArgentinePeso = NumberFormat._(34, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ArgentinePeso'); - static const NumberFormat UruguayanPeso = NumberFormat._(35, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UruguayanPeso'); - static const NumberFormat Percent = NumberFormat._(36, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Percent'); - - static const $core.List values = [ - Number, - USD, - CanadianDollar, - EUR, - Pound, - Yen, - Ruble, - Rupee, - Won, - Yuan, - Real, - Lira, - Rupiah, - Franc, - HongKongDollar, - NewZealandDollar, - Krona, - NorwegianKrone, - MexicanPeso, - Rand, - NewTaiwanDollar, - DanishKrone, - Baht, - Forint, - Koruna, - Shekel, - ChileanPeso, - PhilippinePeso, - Dirham, - ColombianPeso, - Riyal, - Ringgit, - Leu, - ArgentinePeso, - UruguayanPeso, - Percent, - ]; - - static final $core.Map<$core.int, NumberFormat> _byValue = $pb.ProtobufEnum.initByValue(values); - static NumberFormat? valueOf($core.int value) => _byValue[value]; - - const NumberFormat._($core.int v, $core.String n) : super(v, n); -} - diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbjson.dart index f5e89367e2..492dc3689a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/number_type_option.pbjson.dart @@ -8,51 +8,6 @@ import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; -@$core.Deprecated('Use numberFormatDescriptor instead') -const NumberFormat$json = const { - '1': 'NumberFormat', - '2': const [ - const {'1': 'Number', '2': 0}, - const {'1': 'USD', '2': 1}, - const {'1': 'CanadianDollar', '2': 2}, - const {'1': 'EUR', '2': 4}, - const {'1': 'Pound', '2': 5}, - const {'1': 'Yen', '2': 6}, - const {'1': 'Ruble', '2': 7}, - const {'1': 'Rupee', '2': 8}, - const {'1': 'Won', '2': 9}, - const {'1': 'Yuan', '2': 10}, - const {'1': 'Real', '2': 11}, - const {'1': 'Lira', '2': 12}, - const {'1': 'Rupiah', '2': 13}, - const {'1': 'Franc', '2': 14}, - const {'1': 'HongKongDollar', '2': 15}, - const {'1': 'NewZealandDollar', '2': 16}, - const {'1': 'Krona', '2': 17}, - const {'1': 'NorwegianKrone', '2': 18}, - const {'1': 'MexicanPeso', '2': 19}, - const {'1': 'Rand', '2': 20}, - const {'1': 'NewTaiwanDollar', '2': 21}, - const {'1': 'DanishKrone', '2': 22}, - const {'1': 'Baht', '2': 23}, - const {'1': 'Forint', '2': 24}, - const {'1': 'Koruna', '2': 25}, - const {'1': 'Shekel', '2': 26}, - const {'1': 'ChileanPeso', '2': 27}, - const {'1': 'PhilippinePeso', '2': 28}, - const {'1': 'Dirham', '2': 29}, - const {'1': 'ColombianPeso', '2': 30}, - const {'1': 'Riyal', '2': 31}, - const {'1': 'Ringgit', '2': 32}, - const {'1': 'Leu', '2': 33}, - const {'1': 'ArgentinePeso', '2': 34}, - const {'1': 'UruguayanPeso', '2': 35}, - const {'1': 'Percent', '2': 36}, - ], -}; - -/// Descriptor for `NumberFormat`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List numberFormatDescriptor = $convert.base64Decode('CgxOdW1iZXJGb3JtYXQSCgoGTnVtYmVyEAASBwoDVVNEEAESEgoOQ2FuYWRpYW5Eb2xsYXIQAhIHCgNFVVIQBBIJCgVQb3VuZBAFEgcKA1llbhAGEgkKBVJ1YmxlEAcSCQoFUnVwZWUQCBIHCgNXb24QCRIICgRZdWFuEAoSCAoEUmVhbBALEggKBExpcmEQDBIKCgZSdXBpYWgQDRIJCgVGcmFuYxAOEhIKDkhvbmdLb25nRG9sbGFyEA8SFAoQTmV3WmVhbGFuZERvbGxhchAQEgkKBUtyb25hEBESEgoOTm9yd2VnaWFuS3JvbmUQEhIPCgtNZXhpY2FuUGVzbxATEggKBFJhbmQQFBITCg9OZXdUYWl3YW5Eb2xsYXIQFRIPCgtEYW5pc2hLcm9uZRAWEggKBEJhaHQQFxIKCgZGb3JpbnQQGBIKCgZLb3J1bmEQGRIKCgZTaGVrZWwQGhIPCgtDaGlsZWFuUGVzbxAbEhIKDlBoaWxpcHBpbmVQZXNvEBwSCgoGRGlyaGFtEB0SEQoNQ29sb21iaWFuUGVzbxAeEgkKBVJpeWFsEB8SCwoHUmluZ2dpdBAgEgcKA0xldRAhEhEKDUFyZ2VudGluZVBlc28QIhIRCg1VcnVndWF5YW5QZXNvECMSCwoHUGVyY2VudBAk'); @$core.Deprecated('Use numberTypeOptionDescriptor instead') const NumberTypeOption$json = const { '1': 'NumberTypeOption', diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart index c056e2799a..b0cac042fd 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart @@ -7,6 +7,7 @@ export './row_entities.pb.dart'; export './cell_entities.pb.dart'; export './url_type_option.pb.dart'; export './checkbox_type_option.pb.dart'; +export './format.pb.dart'; export './event_map.pb.dart'; export './text_type_option.pb.dart'; export './date_type_option.pb.dart'; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/format.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/format.rs new file mode 100644 index 0000000000..bba24f9e4c --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/format.rs @@ -0,0 +1,207 @@ +// This file is generated by rust-protobuf 2.25.2. Do not edit +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `format.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2; + +#[derive(Clone,PartialEq,Eq,Debug,Hash)] +pub enum NumberFormat { + Number = 0, + USD = 1, + CanadianDollar = 2, + EUR = 4, + Pound = 5, + Yen = 6, + Ruble = 7, + Rupee = 8, + Won = 9, + Yuan = 10, + Real = 11, + Lira = 12, + Rupiah = 13, + Franc = 14, + HongKongDollar = 15, + NewZealandDollar = 16, + Krona = 17, + NorwegianKrone = 18, + MexicanPeso = 19, + Rand = 20, + NewTaiwanDollar = 21, + DanishKrone = 22, + Baht = 23, + Forint = 24, + Koruna = 25, + Shekel = 26, + ChileanPeso = 27, + PhilippinePeso = 28, + Dirham = 29, + ColombianPeso = 30, + Riyal = 31, + Ringgit = 32, + Leu = 33, + ArgentinePeso = 34, + UruguayanPeso = 35, + Percent = 36, +} + +impl ::protobuf::ProtobufEnum for NumberFormat { + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(NumberFormat::Number), + 1 => ::std::option::Option::Some(NumberFormat::USD), + 2 => ::std::option::Option::Some(NumberFormat::CanadianDollar), + 4 => ::std::option::Option::Some(NumberFormat::EUR), + 5 => ::std::option::Option::Some(NumberFormat::Pound), + 6 => ::std::option::Option::Some(NumberFormat::Yen), + 7 => ::std::option::Option::Some(NumberFormat::Ruble), + 8 => ::std::option::Option::Some(NumberFormat::Rupee), + 9 => ::std::option::Option::Some(NumberFormat::Won), + 10 => ::std::option::Option::Some(NumberFormat::Yuan), + 11 => ::std::option::Option::Some(NumberFormat::Real), + 12 => ::std::option::Option::Some(NumberFormat::Lira), + 13 => ::std::option::Option::Some(NumberFormat::Rupiah), + 14 => ::std::option::Option::Some(NumberFormat::Franc), + 15 => ::std::option::Option::Some(NumberFormat::HongKongDollar), + 16 => ::std::option::Option::Some(NumberFormat::NewZealandDollar), + 17 => ::std::option::Option::Some(NumberFormat::Krona), + 18 => ::std::option::Option::Some(NumberFormat::NorwegianKrone), + 19 => ::std::option::Option::Some(NumberFormat::MexicanPeso), + 20 => ::std::option::Option::Some(NumberFormat::Rand), + 21 => ::std::option::Option::Some(NumberFormat::NewTaiwanDollar), + 22 => ::std::option::Option::Some(NumberFormat::DanishKrone), + 23 => ::std::option::Option::Some(NumberFormat::Baht), + 24 => ::std::option::Option::Some(NumberFormat::Forint), + 25 => ::std::option::Option::Some(NumberFormat::Koruna), + 26 => ::std::option::Option::Some(NumberFormat::Shekel), + 27 => ::std::option::Option::Some(NumberFormat::ChileanPeso), + 28 => ::std::option::Option::Some(NumberFormat::PhilippinePeso), + 29 => ::std::option::Option::Some(NumberFormat::Dirham), + 30 => ::std::option::Option::Some(NumberFormat::ColombianPeso), + 31 => ::std::option::Option::Some(NumberFormat::Riyal), + 32 => ::std::option::Option::Some(NumberFormat::Ringgit), + 33 => ::std::option::Option::Some(NumberFormat::Leu), + 34 => ::std::option::Option::Some(NumberFormat::ArgentinePeso), + 35 => ::std::option::Option::Some(NumberFormat::UruguayanPeso), + 36 => ::std::option::Option::Some(NumberFormat::Percent), + _ => ::std::option::Option::None + } + } + + fn values() -> &'static [Self] { + static values: &'static [NumberFormat] = &[ + NumberFormat::Number, + NumberFormat::USD, + NumberFormat::CanadianDollar, + NumberFormat::EUR, + NumberFormat::Pound, + NumberFormat::Yen, + NumberFormat::Ruble, + NumberFormat::Rupee, + NumberFormat::Won, + NumberFormat::Yuan, + NumberFormat::Real, + NumberFormat::Lira, + NumberFormat::Rupiah, + NumberFormat::Franc, + NumberFormat::HongKongDollar, + NumberFormat::NewZealandDollar, + NumberFormat::Krona, + NumberFormat::NorwegianKrone, + NumberFormat::MexicanPeso, + NumberFormat::Rand, + NumberFormat::NewTaiwanDollar, + NumberFormat::DanishKrone, + NumberFormat::Baht, + NumberFormat::Forint, + NumberFormat::Koruna, + NumberFormat::Shekel, + NumberFormat::ChileanPeso, + NumberFormat::PhilippinePeso, + NumberFormat::Dirham, + NumberFormat::ColombianPeso, + NumberFormat::Riyal, + NumberFormat::Ringgit, + NumberFormat::Leu, + NumberFormat::ArgentinePeso, + NumberFormat::UruguayanPeso, + NumberFormat::Percent, + ]; + values + } + + fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + ::protobuf::reflect::EnumDescriptor::new_pb_name::("NumberFormat", file_descriptor_proto()) + }) + } +} + +impl ::std::marker::Copy for NumberFormat { +} + +impl ::std::default::Default for NumberFormat { + fn default() -> Self { + NumberFormat::Number + } +} + +impl ::protobuf::reflect::ProtobufValue for NumberFormat { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self)) + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x0cformat.proto*\xf8\x03\n\x0cNumberFormat\x12\n\n\x06Number\x10\0\ + \x12\x07\n\x03USD\x10\x01\x12\x12\n\x0eCanadianDollar\x10\x02\x12\x07\n\ + \x03EUR\x10\x04\x12\t\n\x05Pound\x10\x05\x12\x07\n\x03Yen\x10\x06\x12\t\ + \n\x05Ruble\x10\x07\x12\t\n\x05Rupee\x10\x08\x12\x07\n\x03Won\x10\t\x12\ + \x08\n\x04Yuan\x10\n\x12\x08\n\x04Real\x10\x0b\x12\x08\n\x04Lira\x10\x0c\ + \x12\n\n\x06Rupiah\x10\r\x12\t\n\x05Franc\x10\x0e\x12\x12\n\x0eHongKongD\ + ollar\x10\x0f\x12\x14\n\x10NewZealandDollar\x10\x10\x12\t\n\x05Krona\x10\ + \x11\x12\x12\n\x0eNorwegianKrone\x10\x12\x12\x0f\n\x0bMexicanPeso\x10\ + \x13\x12\x08\n\x04Rand\x10\x14\x12\x13\n\x0fNewTaiwanDollar\x10\x15\x12\ + \x0f\n\x0bDanishKrone\x10\x16\x12\x08\n\x04Baht\x10\x17\x12\n\n\x06Forin\ + t\x10\x18\x12\n\n\x06Koruna\x10\x19\x12\n\n\x06Shekel\x10\x1a\x12\x0f\n\ + \x0bChileanPeso\x10\x1b\x12\x12\n\x0ePhilippinePeso\x10\x1c\x12\n\n\x06D\ + irham\x10\x1d\x12\x11\n\rColombianPeso\x10\x1e\x12\t\n\x05Riyal\x10\x1f\ + \x12\x0b\n\x07Ringgit\x10\x20\x12\x07\n\x03Leu\x10!\x12\x11\n\rArgentine\ + Peso\x10\"\x12\x11\n\rUruguayanPeso\x10#\x12\x0b\n\x07Percent\x10$b\x06p\ + roto3\ +"; + +static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; + +fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() +} + +pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) +} diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs index c0f74e1e9c..8c29b9015a 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs @@ -25,6 +25,9 @@ pub use url_type_option::*; mod checkbox_type_option; pub use checkbox_type_option::*; +mod format; +pub use format::*; + mod event_map; pub use event_map::*; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/number_type_option.rs index 790f3eaaf3..8f3c0e70af 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/number_type_option.rs @@ -26,7 +26,7 @@ #[derive(PartialEq,Clone,Default)] pub struct NumberTypeOption { // message fields - pub format: NumberFormat, + pub format: super::format::NumberFormat, pub scale: u32, pub symbol: ::std::string::String, pub sign_positive: bool, @@ -50,15 +50,15 @@ impl NumberTypeOption { // .NumberFormat format = 1; - pub fn get_format(&self) -> NumberFormat { + pub fn get_format(&self) -> super::format::NumberFormat { self.format } pub fn clear_format(&mut self) { - self.format = NumberFormat::Number; + self.format = super::format::NumberFormat::Number; } // Param is passed by value, moved - pub fn set_format(&mut self, v: NumberFormat) { + pub fn set_format(&mut self, v: super::format::NumberFormat) { self.format = v; } @@ -189,7 +189,7 @@ impl ::protobuf::Message for NumberTypeOption { #[allow(unused_variables)] fn compute_size(&self) -> u32 { let mut my_size = 0; - if self.format != NumberFormat::Number { + if self.format != super::format::NumberFormat::Number { my_size += ::protobuf::rt::enum_size(1, self.format); } if self.scale != 0 { @@ -210,7 +210,7 @@ impl ::protobuf::Message for NumberTypeOption { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { - if self.format != NumberFormat::Number { + if self.format != super::format::NumberFormat::Number { os.write_enum(1, ::protobuf::ProtobufEnum::value(&self.format))?; } if self.scale != 0 { @@ -263,7 +263,7 @@ impl ::protobuf::Message for NumberTypeOption { static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( "format", |m: &NumberTypeOption| { &m.format }, |m: &mut NumberTypeOption| { &mut m.format }, @@ -304,7 +304,7 @@ impl ::protobuf::Message for NumberTypeOption { impl ::protobuf::Clear for NumberTypeOption { fn clear(&mut self) { - self.format = NumberFormat::Number; + self.format = super::format::NumberFormat::Number; self.scale = 0; self.symbol.clear(); self.sign_positive = false; @@ -325,179 +325,13 @@ impl ::protobuf::reflect::ProtobufValue for NumberTypeOption { } } -#[derive(Clone,PartialEq,Eq,Debug,Hash)] -pub enum NumberFormat { - Number = 0, - USD = 1, - CanadianDollar = 2, - EUR = 4, - Pound = 5, - Yen = 6, - Ruble = 7, - Rupee = 8, - Won = 9, - Yuan = 10, - Real = 11, - Lira = 12, - Rupiah = 13, - Franc = 14, - HongKongDollar = 15, - NewZealandDollar = 16, - Krona = 17, - NorwegianKrone = 18, - MexicanPeso = 19, - Rand = 20, - NewTaiwanDollar = 21, - DanishKrone = 22, - Baht = 23, - Forint = 24, - Koruna = 25, - Shekel = 26, - ChileanPeso = 27, - PhilippinePeso = 28, - Dirham = 29, - ColombianPeso = 30, - Riyal = 31, - Ringgit = 32, - Leu = 33, - ArgentinePeso = 34, - UruguayanPeso = 35, - Percent = 36, -} - -impl ::protobuf::ProtobufEnum for NumberFormat { - fn value(&self) -> i32 { - *self as i32 - } - - fn from_i32(value: i32) -> ::std::option::Option { - match value { - 0 => ::std::option::Option::Some(NumberFormat::Number), - 1 => ::std::option::Option::Some(NumberFormat::USD), - 2 => ::std::option::Option::Some(NumberFormat::CanadianDollar), - 4 => ::std::option::Option::Some(NumberFormat::EUR), - 5 => ::std::option::Option::Some(NumberFormat::Pound), - 6 => ::std::option::Option::Some(NumberFormat::Yen), - 7 => ::std::option::Option::Some(NumberFormat::Ruble), - 8 => ::std::option::Option::Some(NumberFormat::Rupee), - 9 => ::std::option::Option::Some(NumberFormat::Won), - 10 => ::std::option::Option::Some(NumberFormat::Yuan), - 11 => ::std::option::Option::Some(NumberFormat::Real), - 12 => ::std::option::Option::Some(NumberFormat::Lira), - 13 => ::std::option::Option::Some(NumberFormat::Rupiah), - 14 => ::std::option::Option::Some(NumberFormat::Franc), - 15 => ::std::option::Option::Some(NumberFormat::HongKongDollar), - 16 => ::std::option::Option::Some(NumberFormat::NewZealandDollar), - 17 => ::std::option::Option::Some(NumberFormat::Krona), - 18 => ::std::option::Option::Some(NumberFormat::NorwegianKrone), - 19 => ::std::option::Option::Some(NumberFormat::MexicanPeso), - 20 => ::std::option::Option::Some(NumberFormat::Rand), - 21 => ::std::option::Option::Some(NumberFormat::NewTaiwanDollar), - 22 => ::std::option::Option::Some(NumberFormat::DanishKrone), - 23 => ::std::option::Option::Some(NumberFormat::Baht), - 24 => ::std::option::Option::Some(NumberFormat::Forint), - 25 => ::std::option::Option::Some(NumberFormat::Koruna), - 26 => ::std::option::Option::Some(NumberFormat::Shekel), - 27 => ::std::option::Option::Some(NumberFormat::ChileanPeso), - 28 => ::std::option::Option::Some(NumberFormat::PhilippinePeso), - 29 => ::std::option::Option::Some(NumberFormat::Dirham), - 30 => ::std::option::Option::Some(NumberFormat::ColombianPeso), - 31 => ::std::option::Option::Some(NumberFormat::Riyal), - 32 => ::std::option::Option::Some(NumberFormat::Ringgit), - 33 => ::std::option::Option::Some(NumberFormat::Leu), - 34 => ::std::option::Option::Some(NumberFormat::ArgentinePeso), - 35 => ::std::option::Option::Some(NumberFormat::UruguayanPeso), - 36 => ::std::option::Option::Some(NumberFormat::Percent), - _ => ::std::option::Option::None - } - } - - fn values() -> &'static [Self] { - static values: &'static [NumberFormat] = &[ - NumberFormat::Number, - NumberFormat::USD, - NumberFormat::CanadianDollar, - NumberFormat::EUR, - NumberFormat::Pound, - NumberFormat::Yen, - NumberFormat::Ruble, - NumberFormat::Rupee, - NumberFormat::Won, - NumberFormat::Yuan, - NumberFormat::Real, - NumberFormat::Lira, - NumberFormat::Rupiah, - NumberFormat::Franc, - NumberFormat::HongKongDollar, - NumberFormat::NewZealandDollar, - NumberFormat::Krona, - NumberFormat::NorwegianKrone, - NumberFormat::MexicanPeso, - NumberFormat::Rand, - NumberFormat::NewTaiwanDollar, - NumberFormat::DanishKrone, - NumberFormat::Baht, - NumberFormat::Forint, - NumberFormat::Koruna, - NumberFormat::Shekel, - NumberFormat::ChileanPeso, - NumberFormat::PhilippinePeso, - NumberFormat::Dirham, - NumberFormat::ColombianPeso, - NumberFormat::Riyal, - NumberFormat::Ringgit, - NumberFormat::Leu, - NumberFormat::ArgentinePeso, - NumberFormat::UruguayanPeso, - NumberFormat::Percent, - ]; - values - } - - fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { - static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT; - descriptor.get(|| { - ::protobuf::reflect::EnumDescriptor::new_pb_name::("NumberFormat", file_descriptor_proto()) - }) - } -} - -impl ::std::marker::Copy for NumberFormat { -} - -impl ::std::default::Default for NumberFormat { - fn default() -> Self { - NumberFormat::Number - } -} - -impl ::protobuf::reflect::ProtobufValue for NumberFormat { - fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { - ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self)) - } -} - static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x18number_type_option.proto\"\xa0\x01\n\x10NumberTypeOption\x12%\n\ - \x06format\x18\x01\x20\x01(\x0e2\r.NumberFormatR\x06format\x12\x14\n\x05\ - scale\x18\x02\x20\x01(\rR\x05scale\x12\x16\n\x06symbol\x18\x03\x20\x01(\ - \tR\x06symbol\x12#\n\rsign_positive\x18\x04\x20\x01(\x08R\x0csignPositiv\ - e\x12\x12\n\x04name\x18\x05\x20\x01(\tR\x04name*\xf8\x03\n\x0cNumberForm\ - at\x12\n\n\x06Number\x10\0\x12\x07\n\x03USD\x10\x01\x12\x12\n\x0eCanadia\ - nDollar\x10\x02\x12\x07\n\x03EUR\x10\x04\x12\t\n\x05Pound\x10\x05\x12\ - \x07\n\x03Yen\x10\x06\x12\t\n\x05Ruble\x10\x07\x12\t\n\x05Rupee\x10\x08\ - \x12\x07\n\x03Won\x10\t\x12\x08\n\x04Yuan\x10\n\x12\x08\n\x04Real\x10\ - \x0b\x12\x08\n\x04Lira\x10\x0c\x12\n\n\x06Rupiah\x10\r\x12\t\n\x05Franc\ - \x10\x0e\x12\x12\n\x0eHongKongDollar\x10\x0f\x12\x14\n\x10NewZealandDoll\ - ar\x10\x10\x12\t\n\x05Krona\x10\x11\x12\x12\n\x0eNorwegianKrone\x10\x12\ - \x12\x0f\n\x0bMexicanPeso\x10\x13\x12\x08\n\x04Rand\x10\x14\x12\x13\n\ - \x0fNewTaiwanDollar\x10\x15\x12\x0f\n\x0bDanishKrone\x10\x16\x12\x08\n\ - \x04Baht\x10\x17\x12\n\n\x06Forint\x10\x18\x12\n\n\x06Koruna\x10\x19\x12\ - \n\n\x06Shekel\x10\x1a\x12\x0f\n\x0bChileanPeso\x10\x1b\x12\x12\n\x0ePhi\ - lippinePeso\x10\x1c\x12\n\n\x06Dirham\x10\x1d\x12\x11\n\rColombianPeso\ - \x10\x1e\x12\t\n\x05Riyal\x10\x1f\x12\x0b\n\x07Ringgit\x10\x20\x12\x07\n\ - \x03Leu\x10!\x12\x11\n\rArgentinePeso\x10\"\x12\x11\n\rUruguayanPeso\x10\ - #\x12\x0b\n\x07Percent\x10$b\x06proto3\ + \n\x18number_type_option.proto\x1a\x0cformat.proto\"\xa0\x01\n\x10Number\ + TypeOption\x12%\n\x06format\x18\x01\x20\x01(\x0e2\r.NumberFormatR\x06for\ + mat\x12\x14\n\x05scale\x18\x02\x20\x01(\rR\x05scale\x12\x16\n\x06symbol\ + \x18\x03\x20\x01(\tR\x06symbol\x12#\n\rsign_positive\x18\x04\x20\x01(\ + \x08R\x0csignPositive\x12\x12\n\x04name\x18\x05\x20\x01(\tR\x04nameb\x06\ + proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/format.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/format.proto new file mode 100644 index 0000000000..6a1e497aaf --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/format.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +enum NumberFormat { + Number = 0; + USD = 1; + CanadianDollar = 2; + EUR = 4; + Pound = 5; + Yen = 6; + Ruble = 7; + Rupee = 8; + Won = 9; + Yuan = 10; + Real = 11; + Lira = 12; + Rupiah = 13; + Franc = 14; + HongKongDollar = 15; + NewZealandDollar = 16; + Krona = 17; + NorwegianKrone = 18; + MexicanPeso = 19; + Rand = 20; + NewTaiwanDollar = 21; + DanishKrone = 22; + Baht = 23; + Forint = 24; + Koruna = 25; + Shekel = 26; + ChileanPeso = 27; + PhilippinePeso = 28; + Dirham = 29; + ColombianPeso = 30; + Riyal = 31; + Ringgit = 32; + Leu = 33; + ArgentinePeso = 34; + UruguayanPeso = 35; + Percent = 36; +} diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/number_type_option.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/number_type_option.proto index af08761ad9..47a76e40cd 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/number_type_option.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/number_type_option.proto @@ -1,4 +1,5 @@ syntax = "proto3"; +import "format.proto"; message NumberTypeOption { NumberFormat format = 1; @@ -7,41 +8,3 @@ message NumberTypeOption { bool sign_positive = 4; string name = 5; } -enum NumberFormat { - Number = 0; - USD = 1; - CanadianDollar = 2; - EUR = 4; - Pound = 5; - Yen = 6; - Ruble = 7; - Rupee = 8; - Won = 9; - Yuan = 10; - Real = 11; - Lira = 12; - Rupiah = 13; - Franc = 14; - HongKongDollar = 15; - NewZealandDollar = 16; - Krona = 17; - NorwegianKrone = 18; - MexicanPeso = 19; - Rand = 20; - NewTaiwanDollar = 21; - DanishKrone = 22; - Baht = 23; - Forint = 24; - Koruna = 25; - Shekel = 26; - ChileanPeso = 27; - PhilippinePeso = 28; - Dirham = 29; - ColombianPeso = 30; - Riyal = 31; - Ringgit = 32; - Leu = 33; - ArgentinePeso = 34; - UruguayanPeso = 35; - Percent = 36; -} 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/format.rs similarity index 54% rename from frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option.rs rename to frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/format.rs index cf4bd22e3d..149404fa9f 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/format.rs @@ -1,182 +1,16 @@ -use crate::impl_type_option; -use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; -use bytes::Bytes; -use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::entities::{ - CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, -}; +use flowy_derive::ProtoBuf_Enum; use lazy_static::lazy_static; -use rust_decimal::Decimal; + use rusty_money::define_currency_set; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use strum::IntoEnumIterator; use strum_macros::EnumIter; lazy_static! { - static ref STRIP_SYMBOL: Vec = make_strip_symbol(); -} - -#[derive(Default)] -pub struct NumberTypeOptionBuilder(NumberTypeOption); -impl_into_box_type_option_builder!(NumberTypeOptionBuilder); -impl_builder_from_json_str_and_from_bytes!(NumberTypeOptionBuilder, NumberTypeOption); - -impl NumberTypeOptionBuilder { - pub fn name(mut self, name: &str) -> Self { - self.0.name = name.to_string(); - self - } - - pub fn set_format(mut self, format: NumberFormat) -> Self { - self.0.set_format(format); - self - } - - pub fn scale(mut self, scale: u32) -> Self { - self.0.scale = scale; - self - } - - pub fn positive(mut self, positive: bool) -> Self { - self.0.sign_positive = positive; - self - } -} - -impl TypeOptionBuilder for NumberTypeOptionBuilder { - fn field_type(&self) -> FieldType { - self.0.field_type() - } - - fn entry(&self) -> &dyn TypeOptionDataEntry { - &self.0 - } -} - -// Number -#[derive(Clone, Debug, Serialize, Deserialize, ProtoBuf)] -pub struct NumberTypeOption { - #[pb(index = 1)] - pub format: NumberFormat, - - #[pb(index = 2)] - pub scale: u32, - - #[pb(index = 3)] - pub symbol: String, - - #[pb(index = 4)] - pub sign_positive: bool, - - #[pb(index = 5)] - pub name: String, -} -impl_type_option!(NumberTypeOption, FieldType::Number); - -impl CellDataOperation for NumberTypeOption { - fn decode_cell_data( - &self, - encoded_data: T, - decoded_field_type: &FieldType, - _field_meta: &FieldMeta, - ) -> FlowyResult - where - T: Into, - { - if decoded_field_type.is_date() { - return Ok(DecodedCellData::default()); - } - - let cell_data = encoded_data.into(); - match self.format { - NumberFormat::Number => { - if let Ok(v) = cell_data.parse::() { - return Ok(DecodedCellData::new(v.to_string())); - } - - if let Ok(v) = cell_data.parse::() { - return Ok(DecodedCellData::new(v.to_string())); - } - - Ok(DecodedCellData::default()) - } - NumberFormat::Percent => { - let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); - Ok(DecodedCellData::new(content)) - } - _ => { - let content = self.money_from_str(&cell_data); - Ok(DecodedCellData::new(content)) - } - } - } - - fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result - where - C: Into, - { - let changeset = changeset.into(); - let mut data = changeset.trim().to_string(); - - if self.format != NumberFormat::Number { - data = self.strip_symbol(data); - if !data.chars().all(char::is_numeric) { - return Err(FlowyError::invalid_data().context("Should only contain numbers")); - } - } - - Ok(data) - } -} - -impl std::default::Default for NumberTypeOption { - fn default() -> Self { - let format = NumberFormat::default(); - let symbol = format.symbol(); - NumberTypeOption { - format, - scale: 0, - symbol, - sign_positive: true, - name: "Number".to_string(), - } - } -} - -impl NumberTypeOption { - pub fn set_format(&mut self, format: NumberFormat) { - self.format = format; - self.symbol = format.symbol(); - } - - fn money_from_str(&self, s: &str) -> String { - match Decimal::from_str(s) { - Ok(mut decimal) => { - match decimal.set_scale(self.scale) { - Ok(_) => {} - Err(e) => { - tracing::error!("Set decimal scale failed: {:?}", e); - } - } - - decimal.set_sign_positive(self.sign_positive); - let money = rusty_money::Money::from_decimal(decimal, self.format.currency()); - money.to_string() - } - Err(_) => String::new(), - } - } - - fn strip_symbol(&self, s: T) -> String { - let mut s = s.to_string(); - if !s.chars().all(char::is_numeric) { - s.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); - } - s - } + pub static ref CURRENCY_SYMBOL: Vec = NumberFormat::iter() + .map(|format| format.symbol()) + .collect::>(); + pub static ref STRIP_SYMBOL: Vec = vec![",".to_owned(), ".".to_owned()]; } #[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)] @@ -609,137 +443,3 @@ impl NumberFormat { self.currency().symbol.to_string() } } - -fn make_strip_symbol() -> Vec { - let mut symbols = vec![",".to_owned(), ".".to_owned()]; - for format in NumberFormat::iter() { - symbols.push(format.symbol()); - } - symbols -} - -#[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(cell_data, field_type, field_meta) - .unwrap() - .to_string(), - expected_str.to_owned() - ); - } -} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs new file mode 100644 index 0000000000..fffbad97bf --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs @@ -0,0 +1,6 @@ +#![allow(clippy::module_inception)] +mod format; +mod number_type_option; + +pub use format::*; +pub use number_type_option::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs new file mode 100644 index 0000000000..f1000cbcfa --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -0,0 +1,348 @@ +use crate::impl_type_option; + +use crate::services::field::type_options::number_type_option::format::*; +use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; +use bytes::Bytes; +use flowy_derive::ProtoBuf; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_grid_data_model::entities::{ + CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, +}; +use rust_decimal::Decimal; + +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(Default)] +pub struct NumberTypeOptionBuilder(NumberTypeOption); +impl_into_box_type_option_builder!(NumberTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(NumberTypeOptionBuilder, NumberTypeOption); + +impl NumberTypeOptionBuilder { + pub fn name(mut self, name: &str) -> Self { + self.0.name = name.to_string(); + self + } + + pub fn set_format(mut self, format: NumberFormat) -> Self { + self.0.set_format(format); + self + } + + pub fn scale(mut self, scale: u32) -> Self { + self.0.scale = scale; + self + } + + pub fn positive(mut self, positive: bool) -> Self { + self.0.sign_positive = positive; + self + } +} + +impl TypeOptionBuilder for NumberTypeOptionBuilder { + fn field_type(&self) -> FieldType { + self.0.field_type() + } + + fn entry(&self) -> &dyn TypeOptionDataEntry { + &self.0 + } +} + +// Number +#[derive(Clone, Debug, Serialize, Deserialize, ProtoBuf)] +pub struct NumberTypeOption { + #[pb(index = 1)] + pub format: NumberFormat, + + #[pb(index = 2)] + pub scale: u32, + + #[pb(index = 3)] + pub symbol: String, + + #[pb(index = 4)] + pub sign_positive: bool, + + #[pb(index = 5)] + pub name: String, +} +impl_type_option!(NumberTypeOption, FieldType::Number); + +impl CellDataOperation for NumberTypeOption { + fn decode_cell_data( + &self, + encoded_data: T, + decoded_field_type: &FieldType, + _field_meta: &FieldMeta, + ) -> FlowyResult + where + T: Into, + { + if decoded_field_type.is_date() { + return Ok(DecodedCellData::default()); + } + + let cell_data = encoded_data.into(); + match self.format { + NumberFormat::Number => { + if let Ok(v) = cell_data.parse::() { + return Ok(DecodedCellData::new(v.to_string())); + } + + if let Ok(v) = cell_data.parse::() { + return Ok(DecodedCellData::new(v.to_string())); + } + + Ok(DecodedCellData::default()) + } + NumberFormat::Percent => { + let content = cell_data.parse::().map_or(String::new(), |v| v.to_string()); + Ok(DecodedCellData::new(content)) + } + _ => { + let content = self.number_from_str(&cell_data); + Ok(DecodedCellData::new(content)) + } + } + } + + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + where + C: Into, + { + let changeset = changeset.into(); + let mut data = changeset.trim().to_string(); + + if self.format != NumberFormat::Number { + data = self.strip_symbol(data); + if !data.chars().all(char::is_numeric) { + return Err(FlowyError::invalid_data().context("Should only contain numbers")); + } + } + + Ok(data) + } +} + +impl std::default::Default for NumberTypeOption { + fn default() -> Self { + let format = NumberFormat::default(); + let symbol = format.symbol(); + NumberTypeOption { + format, + scale: 0, + symbol, + sign_positive: true, + name: "Number".to_string(), + } + } +} + +impl NumberTypeOption { + pub fn set_format(&mut self, format: NumberFormat) { + self.format = format; + self.symbol = format.symbol(); + } + + fn number_from_str(&self, s: &str) -> String { + match Decimal::from_str(s) { + Ok(mut decimal) => { + match decimal.set_scale(self.scale) { + Ok(_) => {} + Err(e) => { + tracing::error!("Set decimal scale failed: {:?}", e); + } + } + + decimal.set_sign_positive(self.sign_positive); + let money = rusty_money::Money::from_decimal(decimal, self.format.currency()); + money.to_string() + } + Err(_) => { + let s = self.strip_symbol(s); + if !s.is_empty() && s.chars().all(char::is_numeric) { + self.number_from_str(&s) + } else { + "".to_owned() + } + } + } + } + + fn strip_symbol(&self, s: T) -> String { + let mut s = s.to_string(); + + for symbol in CURRENCY_SYMBOL.iter() { + if s.starts_with(symbol) { + s = s.strip_prefix(symbol).unwrap_or("").to_string(); + break; + } + } + + if !s.chars().all(char::is_numeric) { + s.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); + } + s + } +} + +#[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_type_option_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_type_option_format_number_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); + } + 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_type_option_format_str_test() { + let mut type_option = NumberTypeOption::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, "$18,44", "$1,844", &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, "¥18,44", "¥1,844", &field_type, &field_meta); + assert_equal(&type_option, "¥1844", "¥1,844", &field_type, &field_meta); + } + NumberFormat::Yuan => { + assert_equal(&type_option, "CN¥18,44", "CN¥1,844", &field_type, &field_meta); + assert_equal(&type_option, "CN¥1844", "CN¥1,844", &field_type, &field_meta); + } + NumberFormat::EUR => { + assert_equal(&type_option, "€18.44", "€1.844", &field_type, &field_meta); + assert_equal(&type_option, "€1844", "€1.844", &field_type, &field_meta); + } + _ => {} + } + } + } + + #[test] + fn number_type_option_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(cell_data, field_type, field_meta) + .unwrap() + .to_string(), + expected_str.to_owned() + ); + } +} From 76da44999c3a8220de05cb2ba64b66ca3dcfbded Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 30 May 2022 20:43:38 +0800 Subject: [PATCH 40/82] chore: reset content when number is not valid --- .../grid/cell/cell_service/data_loader.dart | 18 ++++--- .../grid/cell/number_cell_bloc.dart | 30 +++++++----- .../grid/src/widgets/cell/number_cell.dart | 47 ++++++++++--------- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart index 92caedc4e9..676e3f66d0 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart @@ -58,11 +58,7 @@ class GridCellDataLoader extends IGridCellDataLoader { return fut.then( (result) => result.fold((Cell cell) { try { - if (cell.data.isEmpty) { - return null; - } else { - return parser.parserData(cell.data); - } + return parser.parserData(cell.data); } catch (e, s) { Log.error('$parser parser cellData failed, $e'); Log.error('Stack trace \n $s'); @@ -102,13 +98,17 @@ class SelectOptionCellDataLoader extends IGridCellDataLoader { @override String? parserData(List data) { - return utf8.decode(data); + final s = utf8.decode(data); + return s; } } class DateCellDataParser implements ICellDataParser { @override DateCellData? parserData(List data) { + if (data.isEmpty) { + return null; + } return DateCellData.fromBuffer(data); } } @@ -116,6 +116,9 @@ class DateCellDataParser implements ICellDataParser { class SelectOptionCellDataParser implements ICellDataParser { @override SelectOptionCellData? parserData(List data) { + if (data.isEmpty) { + return null; + } return SelectOptionCellData.fromBuffer(data); } } @@ -123,6 +126,9 @@ class SelectOptionCellDataParser implements ICellDataParser { @override URLCellData? parserData(List data) { + if (data.isEmpty) { + return null; + } return URLCellData.fromBuffer(data); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart index b694ac5059..adcfee71e6 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart @@ -1,7 +1,8 @@ -import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; +import 'package:dartz/dartz.dart'; import 'cell_service/cell_service.dart'; part 'number_cell_bloc.freezed.dart'; @@ -15,16 +16,21 @@ class NumberCellBloc extends Bloc { }) : super(NumberCellState.initial(cellContext)) { on( (event, emit) async { - await event.when( - initial: () async { + event.when( + initial: () { _startListening(); }, - didReceiveCellUpdate: (cellContent) { - emit(state.copyWith(content: cellContent ?? "")); + didReceiveCellUpdate: (content) { + emit(state.copyWith(content: content)); }, - updateCell: (text) async { + updateCell: (text) { cellContext.saveCellData(text, resultCallback: (result) { - result.fold(() => null, (err) => Log.error(err)); + result.fold( + () => null, + (err) { + if (!isClosed) add(NumberCellEvent.didReceiveCellUpdate(right(err))); + }, + ); }); }, ); @@ -46,7 +52,7 @@ class NumberCellBloc extends Bloc { _onCellChangedFn = cellContext.startListening( onCellChanged: ((cellContent) { if (!isClosed) { - add(NumberCellEvent.didReceiveCellUpdate(cellContent)); + add(NumberCellEvent.didReceiveCellUpdate(left(cellContent ?? ""))); } }), ); @@ -57,17 +63,19 @@ class NumberCellBloc extends Bloc { class NumberCellEvent with _$NumberCellEvent { const factory NumberCellEvent.initial() = _Initial; const factory NumberCellEvent.updateCell(String text) = _UpdateCell; - const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) = _DidReceiveCellUpdate; + const factory NumberCellEvent.didReceiveCellUpdate(Either cellContent) = _DidReceiveCellUpdate; } @freezed class NumberCellState with _$NumberCellState { const factory NumberCellState({ - required String content, + required Either content, }) = _NumberCellState; factory NumberCellState.initial(GridCellContext context) { final cellContent = context.getCellData() ?? ""; - return NumberCellState(content: cellContent); + return NumberCellState( + content: left(cellContent), + ); } } 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 da0b222d25..d11229c4cb 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 @@ -29,7 +29,7 @@ class _NumberCellState extends State { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); - _controller = TextEditingController(text: _cellBloc.state.content); + _controller = TextEditingController(text: contentFromState(_cellBloc.state)); _focusNode = CellSingleFocusNode(); _listenFocusNode(); super.initState(); @@ -40,26 +40,25 @@ class _NumberCellState extends State { _listenCellRequestFocus(context); return BlocProvider.value( value: _cellBloc, - child: BlocConsumer( - listener: (context, state) { - if (_controller.text != state.content) { - _controller.text = state.content; - } - }, - builder: (context, state) { - return TextField( - controller: _controller, - focusNode: _focusNode, - onEditingComplete: () => _focusNode.unfocus(), - maxLines: null, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), - decoration: const InputDecoration( - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - isDense: true, - ), - ); - }, + child: MultiBlocListener( + listeners: [ + BlocListener( + listenWhen: (p, c) => p.content != c.content, + listener: (context, state) => _controller.text = contentFromState(state), + ), + ], + child: TextField( + controller: _controller, + focusNode: _focusNode, + onEditingComplete: () => _focusNode.unfocus(), + maxLines: null, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + isDense: true, + ), + ), ), ); } @@ -86,7 +85,7 @@ class _NumberCellState extends State { if (mounted) { _delayOperation?.cancel(); _delayOperation = Timer(const Duration(milliseconds: 300), () { - if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { + if (_cellBloc.isClosed == false && _controller.text != contentFromState(_cellBloc.state)) { _cellBloc.add(NumberCellEvent.updateCell(_controller.text)); } }); @@ -108,4 +107,8 @@ class _NumberCellState extends State { } }); } + + String contentFromState(NumberCellState state) { + return state.content.fold((l) => l, (r) => ""); + } } From 14b012686bf31e933b5c68e69099565d9a9730a3 Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Mon, 30 May 2022 20:48:28 +0800 Subject: [PATCH 41/82] feat: native support for m1 mac --- .github/workflows/ci.yaml | 2 +- .github/workflows/release.yml | 2 +- frontend/Makefile.toml | 14 +++++++++++--- .../macos/Runner.xcodeproj/project.pbxproj | 6 +++--- frontend/scripts/build_sdk.sh | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ab1becf24f..d3dd050e2c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: - os: ubuntu-latest flutter_profile: development-linux-x86 - os: macos-latest - flutter_profile: development-mac + flutter_profile: development-mac-x86_64 runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d500c4659..a2c9c7b133 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,7 +113,7 @@ jobs: working-directory: frontend run: | flutter config --enable-macos-desktop - cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86 appflowy + cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy - name: Archive macOS app working-directory: ${{ env.MACOS_APP_RELEASE_PATH }} diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index c46687a2df..0dbc58d572 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -45,7 +45,15 @@ APP_ENVIRONMENT = "local" FLUTTER_FLOWY_SDK_PATH="app_flowy/packages/flowy_sdk" PROTOBUF_DERIVE_CACHE="../shared-lib/flowy-derive/src/derive_cache/derive_cache.rs" -[env.development-mac] +[env.development-mac-arm64] +RUST_LOG = "info" +TARGET_OS = "macos" +RUST_COMPILE_TARGET = "aarch64-apple-darwin" +BUILD_FLAG = "debug" +FLUTTER_OUTPUT_DIR = "Debug" +PRODUCT_EXT = "app" + +[env.development-mac-x86_64] RUST_LOG = "info" TARGET_OS = "macos" RUST_COMPILE_TARGET = "x86_64-apple-darwin" @@ -53,7 +61,7 @@ BUILD_FLAG = "debug" FLUTTER_OUTPUT_DIR = "Debug" PRODUCT_EXT = "app" -[env.production-mac-aarch64] +[env.production-mac-arm64] BUILD_FLAG = "release" TARGET_OS = "macos" RUST_COMPILE_TARGET = "aarch64-apple-darwin" @@ -61,7 +69,7 @@ FLUTTER_OUTPUT_DIR = "Release" PRODUCT_EXT = "app" APP_ENVIRONMENT = "production" -[env.production-mac-x86] +[env.production-mac-x86_64] BUILD_FLAG = "release" TARGET_OS = "macos" RUST_COMPILE_TARGET = "x86_64-apple-darwin" diff --git a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj index 2e7ab66fee..61cb05b101 100644 --- a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj +++ b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj @@ -421,7 +421,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - EXCLUDED_ARCHS = arm64; + EXCLUDED_ARCHS = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -553,7 +553,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - EXCLUDED_ARCHS = arm64; + EXCLUDED_ARCHS = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -577,7 +577,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - EXCLUDED_ARCHS = arm64; + EXCLUDED_ARCHS = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/frontend/scripts/build_sdk.sh b/frontend/scripts/build_sdk.sh index 061fa58d0a..330bd22738 100755 --- a/frontend/scripts/build_sdk.sh +++ b/frontend/scripts/build_sdk.sh @@ -25,7 +25,7 @@ Linux-x86) ;; macOS) - cargo make --profile development-mac flowy-sdk-dev + cargo make --profile "development-mac-$(uname -m)" flowy-sdk-dev ;; Windows) From be103e02faf392f6e30492b2999a32f8feafbe17 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Mon, 30 May 2022 17:10:53 +0200 Subject: [PATCH 42/82] feat: in app notification bubble --- .../workspace/application/doc/share_bloc.dart | 7 --- .../presentation/plugins/doc/document.dart | 4 ++ .../presentation/widgets/dialogs.dart | 43 +++++++++++++------ 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index f5be9f37af..7954521847 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -35,8 +35,6 @@ class DocShareBloc extends Bloc { }); } - bool checkFile = false; - ExportData _convertDeltaToMarkdown(ExportData value) { final result = deltaToMarkdown(value.data); value.data = result; @@ -64,16 +62,11 @@ class DocShareBloc extends Bloc { Future get _localFile async { final path = await _localPath; - checkFile = true; return File('$path/${view.name}.md'); } Future writeFile(String md) async { final file = await _localFile; - if (checkFile) - BubbleNotification(msgTitle: 'Export To Markdown', msgBody: 'File saved to $file'); - else - BubbleNotification(msgTitle: 'Failed to write to file', msgBody: '$file'); return file.writeAsString(md); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart index 5dcf744083..be70d7a67f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart @@ -179,6 +179,10 @@ class DocumentShareButton extends StatelessWidget { switch (action) { case ShareAction.markdown: context.read().add(const DocShareEvent.shareMarkdown()); + BubbleNotification( + msgTitle: 'Exported Complete ^_^', + msgBody: "Check in the flowy folder inside your documents directory") + .show(context); break; case ShareAction.copyLink: FlowyAlertDialog(title: LocaleKeys.shareAction_workInProgress.tr()).show(context); diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index fda8d37d9c..52a506db2c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -221,26 +221,41 @@ class OkCancelButton extends StatelessWidget { } } -class BubbleNotification extends StatelessWidget { +class BubbleNotification extends StatefulWidget { final String msgTitle; final String msgBody; const BubbleNotification({Key? key, required this.msgTitle, required this.msgBody}) : super(key: key); + @override + State createState() => _BubbleNotification(); +} + +class _BubbleNotification extends State { + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { - return Card( - margin: const EdgeInsets.symmetric(horizontal: 4), - child: SafeArea( - child: ListTile( - leading: SizedBox.fromSize( - size: const Size(40, 40), - // child: ClipOval(child: ) - ), - title: Text(msgTitle), - subtitle: Text(msgBody), - ), - // title: Text('Action') - )); + return StyledDialog( + // maxWidth: 800, + maxHeight: 200, + shrinkWrap: true, + child: Card( + margin: const EdgeInsets.symmetric(horizontal: 4), + child: SafeArea( + child: ListTile( + leading: SizedBox.fromSize( + size: const Size(40, 40), + child: ClipOval( + child: Icon(Icons.file_copy), + ), + ), + title: Text(widget.msgTitle), + subtitle: Text(widget.msgBody), + ), + ))); } } From f94684e0b633748c8819569d2c1fa9d33148f3d7 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Mon, 30 May 2022 19:59:49 +0200 Subject: [PATCH 43/82] fix: remove unused imports --- frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart | 1 - .../app_flowy/lib/workspace/presentation/widgets/dialogs.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index 7954521847..5177c93535 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; -import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index 52a506db2c..cae4137ba1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -14,7 +14,6 @@ import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart'; import 'package:textstyle_extensions/textstyle_extensions.dart'; export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; -import 'package:overlay_support/overlay_support.dart'; class TextFieldDialog extends StatefulWidget { final String value; From c4ca0553a9a6f77464db9d823911183995249e68 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Mon, 30 May 2022 20:15:30 +0200 Subject: [PATCH 44/82] chore: add const to remove flutter warnings --- .../lib/workspace/presentation/plugins/doc/document.dart | 2 +- .../app_flowy/lib/workspace/presentation/widgets/dialogs.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart index be70d7a67f..efe4b4bf39 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart @@ -179,7 +179,7 @@ class DocumentShareButton extends StatelessWidget { switch (action) { case ShareAction.markdown: context.read().add(const DocShareEvent.shareMarkdown()); - BubbleNotification( + const BubbleNotification( msgTitle: 'Exported Complete ^_^', msgBody: "Check in the flowy folder inside your documents directory") .show(context); diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index cae4137ba1..c17b386df0 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -248,7 +248,7 @@ class _BubbleNotification extends State { child: ListTile( leading: SizedBox.fromSize( size: const Size(40, 40), - child: ClipOval( + child: const ClipOval( child: Icon(Icons.file_copy), ), ), From ab896cbc8ff27c66b0439d7dee800ad05a49310a Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 31 May 2022 16:14:12 +0800 Subject: [PATCH 45/82] chore: enable drag to expand field's width --- .../grid/field/field_cell_bloc.dart | 19 +++++--- .../application/grid/row/row_bloc.dart | 11 ++--- .../grid/src/widgets/header/field_cell.dart | 17 ++++---- .../field/type_options/text_type_option.rs | 2 +- .../field/type_options/url_type_option.rs | 43 ++++++++++--------- 5 files changed, 50 insertions(+), 42 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart index 06a95ae89b..c7e83cc52e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart @@ -24,13 +24,15 @@ class FieldCellBloc extends Bloc { _startListening(); }, didReceiveFieldUpdate: (field) { - emit(state.copyWith(field: field)); + emit(state.copyWith(field: cellContext.field)); }, - updateWidth: (offset) { - final defaultWidth = state.field.width.toDouble(); - final width = defaultWidth + offset; - if (width > defaultWidth && width < 300) { - _fieldService.updateField(width: width); + startUpdateWidth: (offset) { + final width = state.width + offset; + emit(state.copyWith(width: width)); + }, + endUpdateWidth: () { + if (state.width != state.field.width.toDouble()) { + _fieldService.updateField(width: state.width); } }, ); @@ -61,7 +63,8 @@ class FieldCellBloc extends Bloc { class FieldCellEvent with _$FieldCellEvent { const factory FieldCellEvent.initial() = _InitialCell; const factory FieldCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate; - const factory FieldCellEvent.updateWidth(double offset) = _UpdateWidth; + const factory FieldCellEvent.startUpdateWidth(double offset) = _StartUpdateWidth; + const factory FieldCellEvent.endUpdateWidth() = _EndUpdateWidth; } @freezed @@ -69,10 +72,12 @@ class FieldCellState with _$FieldCellState { const factory FieldCellState({ required String gridId, required Field field, + required double width, }) = _FieldCellState; factory FieldCellState.initial(GridFieldCellContext cellContext) => FieldCellState( gridId: cellContext.gridId, field: cellContext.field, + width: cellContext.field.width.toDouble(), ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart index 3d3e3486a5..d8000e3664 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart @@ -30,7 +30,7 @@ class RowBloc extends Bloc { _rowService.createRow(); }, didReceiveCellDatas: (_DidReceiveCellDatas value) async { - final fields = value.gridCellMap.values.map((e) => CellSnapshot(e.field)).toList(); + final fields = value.gridCellMap.values.map((e) => GridCellEquatable(e.field)).toList(); final snapshots = UnmodifiableListView(fields); emit(state.copyWith( gridCellMap: value.gridCellMap, @@ -74,26 +74,27 @@ class RowState with _$RowState { const factory RowState({ required GridRow rowData, required GridCellMap gridCellMap, - required UnmodifiableListView snapshots, + required UnmodifiableListView snapshots, GridRowChangeReason? changeReason, }) = _RowState; factory RowState.initial(GridRow rowData, GridCellMap cellDataMap) => RowState( rowData: rowData, gridCellMap: cellDataMap, - snapshots: UnmodifiableListView(cellDataMap.values.map((e) => CellSnapshot(e.field)).toList()), + snapshots: UnmodifiableListView(cellDataMap.values.map((e) => GridCellEquatable(e.field)).toList()), ); } -class CellSnapshot extends Equatable { +class GridCellEquatable extends Equatable { final Field _field; - const CellSnapshot(Field field) : _field = field; + const GridCellEquatable(Field field) : _field = field; @override List get props => [ _field.id, _field.fieldType, _field.visibility, + _field.width, ]; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 48866457f7..7e04bdbc4c 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -6,7 +6,6 @@ import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -24,6 +23,7 @@ class GridFieldCell extends StatelessWidget { return BlocProvider( create: (context) => FieldCellBloc(cellContext: cellContext)..add(const FieldCellEvent.initial()), child: BlocBuilder( + // buildWhen: (p, c) => p.field != c.field, builder: (context, state) { final button = FieldCellButton( field: state.field, @@ -38,7 +38,7 @@ class GridFieldCell extends StatelessWidget { ); return _GridHeaderCellContainer( - width: state.field.width.toDouble(), + width: state.width, child: Stack( alignment: Alignment.centerRight, fit: StackFit.expand, @@ -60,13 +60,14 @@ class GridFieldCell extends StatelessWidget { void _showFieldEditor(BuildContext context) { final state = context.read().state; + final field = state.field; FieldEditor( gridId: state.gridId, - fieldName: state.field.name, + fieldName: field.name, contextLoader: FieldContextLoader( gridId: state.gridId, - field: state.field, + field: field, ), ).show(context); } @@ -113,19 +114,17 @@ class _DragToExpandLine extends StatelessWidget { onTap: () {}, child: GestureDetector( behavior: HitTestBehavior.opaque, - onHorizontalDragCancel: () {}, onHorizontalDragUpdate: (value) { - // context.read().add(FieldCellEvent.updateWidth(value.delta.dx)); - Log.info(value); + context.read().add(FieldCellEvent.startUpdateWidth(value.delta.dx)); }, onHorizontalDragEnd: (end) { - Log.info(end); + context.read().add(const FieldCellEvent.endUpdateWidth()); }, child: FlowyHover( style: HoverStyle( hoverColor: theme.main1, borderRadius: BorderRadius.zero, - contentMargin: const EdgeInsets.only(left: 5), + contentMargin: const EdgeInsets.only(left: 6), ), child: const SizedBox(width: 2), ), 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 3acdfd97c5..3509972670 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 @@ -27,7 +27,7 @@ impl TypeOptionBuilder for RichTextTypeOptionBuilder { #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)] pub struct RichTextTypeOption { #[pb(index = 1)] - data: String, //It's not used. + data: String, //It's not used yet } impl_type_option!(RichTextTypeOption, FieldType::RichText); diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 7299b1babd..0d5f37baaf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -30,7 +30,7 @@ impl TypeOptionBuilder for URLTypeOptionBuilder { #[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)] pub struct URLTypeOption { #[pb(index = 1)] - data: String, //It's not used. + data: String, //It's not used yet. } impl_type_option!(URLTypeOption, FieldType::URL); @@ -56,28 +56,31 @@ impl CellDataOperation, String> for URLTypeOption { C: Into, { let changeset = changeset.into(); - let mut cell_data = URLCellData { - url: "".to_string(), - content: changeset.to_string(), - }; - + let mut url = "".to_string(); if let Ok(Some(m)) = URL_REGEX.find(&changeset) { - // Only support https scheme by now - match url::Url::parse(m.as_str()) { - Ok(url) => { - if url.scheme() == "https" { - cell_data.url = url.into(); - } else { - cell_data.url = format!("https://{}", m.as_str()); - } - } - Err(_) => { - cell_data.url = format!("https://{}", m.as_str()); - } + url = auto_append_scheme(m.as_str()); + } + URLCellData { + url, + content: changeset.to_string(), + } + .to_json() + } +} + +fn auto_append_scheme(s: &str) -> String { + // Only support https scheme by now + match url::Url::parse(s) { + Ok(url) => { + if url.scheme() == "https" { + url.into() + } else { + format!("https://{}", s) } } - - cell_data.to_json() + Err(_) => { + format!("https://{}", s) + } } } From 3d41cb0cec9a6482af751a805ddf9b4bb1d0e234 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 31 May 2022 16:22:20 +0800 Subject: [PATCH 46/82] chore: adjust ui color --- .../plugins/grid/src/widgets/header/field_cell.dart | 5 +++-- .../packages/flowy_infra_ui/lib/style_widget/button.dart | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 7e04bdbc4c..21172b38d9 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -85,7 +85,8 @@ class _GridHeaderCellContainer extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); - final borderSide = BorderSide(color: theme.shader4, width: 0.4); + final borderSide = BorderSide(color: theme.shader5, width: 1.0); + final decoration = BoxDecoration( border: Border( top: borderSide, @@ -126,7 +127,7 @@ class _DragToExpandLine extends StatelessWidget { borderRadius: BorderRadius.zero, contentMargin: const EdgeInsets.only(left: 6), ), - child: const SizedBox(width: 2), + child: const SizedBox(width: 4), ), ), ); diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart index 9a7407f9bb..046ee8a0c1 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -1,4 +1,3 @@ -import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; @@ -28,7 +27,7 @@ class FlowyButton extends StatelessWidget { return InkWell( onTap: onTap, child: FlowyHover( - style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: hoverColor), + style: HoverStyle(borderRadius: BorderRadius.zero, hoverColor: hoverColor), setSelected: () => isSelected, builder: (context, onHover) => _render(), ), From 2648a54482ca61f28b3360f5277ac5849765ee4c Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Tue, 31 May 2022 11:41:55 +0200 Subject: [PATCH 47/82] refactor: refactored functions & added translation --- frontend/app_flowy/assets/translations/en.json | 6 ++++++ .../workspace/application/doc/share_bloc.dart | 18 +++++------------- .../presentation/plugins/doc/document.dart | 6 +++--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 4e6c8f3420..ce1f984f52 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -96,6 +96,12 @@ "lightMode": "Switch to Light mode", "darkMode": "Switch to Dark mode" }, + "notifications": { + "export": { + "markdown": "Exported Note To Markdown", + "path": "Documents/flowy" + } + }, "contactsPage": { "title": "Contacts", "whatsHappening": "What's happening this week?", diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index 5177c93535..4e17619b9f 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/startup/tasks/rust_sdk.dart'; import 'package:app_flowy/workspace/application/doc/share_service.dart'; import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart'; import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart'; @@ -8,7 +8,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:dartz/dartz.dart'; part 'share_bloc.freezed.dart'; @@ -41,21 +40,14 @@ class DocShareBloc extends Bloc { return value; } - Future appFlowyDocumentDirectory() async { - Directory documentsDir = await getApplicationDocumentsDirectory(); + Future get _ExportDir async { + Directory documentsDir = await appFlowyDocumentDirectory(); - switch (integrationEnv()) { - case IntegrationMode.develop: - return Directory('${documentsDir.path}/flowy_dev').create(); - case IntegrationMode.release: - return Directory('${documentsDir.path}/flowy').create(); - case IntegrationMode.test: - return Directory("${Directory.current.path}/.sandbox"); - } + return documentsDir; } Future get _localPath async { - final dir = await appFlowyDocumentDirectory(); + final dir = await _ExportDir; return dir.path; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart index efe4b4bf39..7c2a47a8c1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart @@ -179,9 +179,9 @@ class DocumentShareButton extends StatelessWidget { switch (action) { case ShareAction.markdown: context.read().add(const DocShareEvent.shareMarkdown()); - const BubbleNotification( - msgTitle: 'Exported Complete ^_^', - msgBody: "Check in the flowy folder inside your documents directory") + BubbleNotification( + msgTitle: LocaleKeys.notifications_export_markdown.tr(), + msgBody: 'Path: ${LocaleKeys.notifications_export_path.tr()}') .show(context); break; case ShareAction.copyLink: From a568f6323dd066934bbabc86d4c8770c1f0ce53b Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 31 May 2022 21:06:02 +0800 Subject: [PATCH 48/82] chore: refactor grid cell expander --- .../grid/src/widgets/cell/cell_builder.dart | 30 ++++++++++++++-- .../widgets/cell/url_cell/cell_editor.dart | 5 ++- .../src/widgets/cell/url_cell/url_cell.dart | 35 +++++++++---------- .../grid/src/widgets/header/field_cell.dart | 1 - .../grid/src/widgets/row/grid_row.dart | 29 +++++++-------- .../lib/style_widget/hover.dart | 4 ++- 6 files changed, 64 insertions(+), 40 deletions(-) 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 f8189e7f02..705ceb5b48 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 @@ -48,11 +48,19 @@ class BlankCell extends StatelessWidget { } } +abstract class GridCellExpander implements Widget { + void onExpand(BuildContext context); +} + abstract class GridCellWidget implements FlowyHoverWidget { @override final ValueNotifier onFocus = ValueNotifier(false); final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier(); + + GridCellExpander? buildExpander() { + return null; + } } class GridCellRequestFocusNotifier extends ChangeNotifier { @@ -125,7 +133,7 @@ class CellStateNotifier extends ChangeNotifier { class CellContainer extends StatelessWidget { final GridCellWidget child; - final Widget? expander; + final GridCellExpander? expander; final double width; final RegionStateNotifier rowStateNotifier; const CellContainer({ @@ -182,17 +190,33 @@ class CellContainer extends StatelessWidget { class CellEnterRegion extends StatelessWidget { final Widget child; - final Widget expander; + final GridCellExpander expander; const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final theme = context.watch(); + return Selector( selector: (context, notifier) => notifier.onEnter, builder: (context, onEnter, _) { List children = [child]; if (onEnter) { - children.add(expander.positioned(right: 0)); + final hover = FlowyHover( + style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface), + builder: (_, onHover) => Container( + width: 26, + height: 26, + padding: const EdgeInsets.all(3), + child: expander, + ), + ); + + children.add(GestureDetector( + child: hover, + behavior: HitTestBehavior.opaque, + onTap: () => expander.onExpand(context), + ).positioned(right: 0)); } return MouseRegion( diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart index 055a4947c8..c16e18906f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart @@ -25,7 +25,10 @@ class URLCellEditor extends StatefulWidget { // FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( - child: SizedBox(width: 200, child: editor), + child: SizedBox( + width: 200, + child: Padding(padding: const EdgeInsets.all(6), child: editor), + ), constraints: BoxConstraints.loose(const Size(300, 160)), ), identifier: URLCellEditor.identifier(), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 917cee66d2..fe708c710a 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -36,6 +37,12 @@ class GridURLCell extends StatefulWidget with GridCellWidget { @override State createState() => _GridURLCellState(); + + @override + GridCellExpander? buildExpander() { + final cellContext = cellContextBuilder.build() as GridURLCellContext; + return _EditURLCellIndicator(cellContext: cellContext); + } } class _GridURLCellState extends State { @@ -70,13 +77,7 @@ class _GridURLCellState extends State { ), ); - return CellEnterRegion( - child: Align(alignment: Alignment.centerLeft, child: richText), - expander: _EditCellIndicator(onTap: () { - final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; - URLCellEditor.show(context, cellContext); - }), - ); + return Align(alignment: Alignment.centerLeft, child: richText); }, ), ); @@ -115,20 +116,18 @@ class _GridURLCellState extends State { } } -class _EditCellIndicator extends StatelessWidget { - final VoidCallback onTap; - const _EditCellIndicator({required this.onTap, Key? key}) : super(key: key); +class _EditURLCellIndicator extends StatelessWidget with GridCellExpander { + final GridURLCellContext cellContext; + const _EditURLCellIndicator({required this.cellContext, Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); - return FlowyIconButton( - width: 26, - onPressed: onTap, - hoverColor: theme.hover, - radius: BorderRadius.circular(4), - iconPadding: const EdgeInsets.all(5), - icon: svgWidget("editor/edit", color: theme.iconColor), - ); + return svgWidget("editor/edit", color: theme.iconColor); + } + + @override + void onExpand(BuildContext context) { + URLCellEditor.show(context, cellContext); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 21172b38d9..5071161d5a 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -86,7 +86,6 @@ class _GridHeaderCellContainer extends StatelessWidget { Widget build(BuildContext context) { final theme = context.watch(); final borderSide = BorderSide(color: theme.shader5, width: 1.0); - final decoration = BoxDecoration( border: Border( top: borderSide, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index a643b58928..32e0ea2663 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -170,14 +170,15 @@ class _RowCells extends StatelessWidget { List _makeCells(BuildContext context, GridCellMap gridCellMap) { return gridCellMap.values.map( (gridCell) { - Widget? expander; + final GridCellWidget child = buildGridCellWidget(gridCell, cellCache); + GridCellExpander? expander = child.buildExpander(); if (gridCell.field.isPrimary) { - expander = _CellExpander(onExpand: onExpand); + expander = _PrimaryCellExpander(onTap: onExpand); } return CellContainer( width: gridCell.field.width.toDouble(), - child: buildGridCellWidget(gridCell, cellCache), + child: child, rowStateNotifier: Provider.of(context, listen: false), expander: expander, ); @@ -199,23 +200,19 @@ class RegionStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } -class _CellExpander extends StatelessWidget { - final VoidCallback onExpand; - const _CellExpander({required this.onExpand, Key? key}) : super(key: key); +class _PrimaryCellExpander extends StatelessWidget with GridCellExpander { + final VoidCallback onTap; + const _PrimaryCellExpander({required this.onTap, Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); - return FittedBox( - fit: BoxFit.contain, - child: FlowyIconButton( - width: 26, - onPressed: onExpand, - iconPadding: const EdgeInsets.all(5), - radius: BorderRadius.circular(4), - icon: svgWidget("grid/expander", color: theme.main1), - ), - ); + return svgWidget("grid/expander", color: theme.main1); + } + + @override + void onExpand(BuildContext context) { + onTap(); } } 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 bb7144974b..5a6ef79470 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 @@ -52,7 +52,7 @@ class _FlowyHoverState extends State { child: child, ); } else { - return child; + return Container(child: child, color: widget.style.backgroundColor); } } } @@ -63,12 +63,14 @@ class HoverStyle { final Color hoverColor; final BorderRadius borderRadius; final EdgeInsets contentMargin; + final Color backgroundColor; const HoverStyle( {this.borderColor = Colors.transparent, this.borderWidth = 0, this.borderRadius = const BorderRadius.all(Radius.circular(6)), this.contentMargin = EdgeInsets.zero, + this.backgroundColor = Colors.transparent, required this.hoverColor}); } From cdfe94cf988a55ba879507d2fccff8e5d2ccae24 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 31 May 2022 21:26:06 +0800 Subject: [PATCH 49/82] chore: rename some classes --- .../grid/src/widgets/cell/cell_builder.dart | 17 +++++++------ .../grid/src/widgets/cell/checkbox_cell.dart | 10 ++++---- .../grid/src/widgets/cell/number_cell.dart | 24 +++++++++---------- .../grid/src/widgets/cell/text_cell.dart | 18 +++++++------- .../src/widgets/cell/url_cell/url_cell.dart | 16 ++++++++----- 5 files changed, 43 insertions(+), 42 deletions(-) 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 705ceb5b48..0fb342bf03 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 @@ -56,24 +56,23 @@ abstract class GridCellWidget implements FlowyHoverWidget { @override final ValueNotifier onFocus = ValueNotifier(false); - final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier(); + final GridCellBeginFocusFocus beginFocus = GridCellBeginFocusFocus(); GridCellExpander? buildExpander() { return null; } } -class GridCellRequestFocusNotifier extends ChangeNotifier { +class GridCellBeginFocusFocus extends ChangeNotifier { VoidCallback? _listener; - @override - void addListener(VoidCallback listener) { + void setListener(VoidCallback listener) { if (_listener != null) { removeListener(_listener!); } _listener = listener; - super.addListener(listener); + addListener(listener); } void removeAllListener() { @@ -89,10 +88,10 @@ class GridCellRequestFocusNotifier extends ChangeNotifier { abstract class GridCellStyle {} -class CellSingleFocusNode extends FocusNode { +class SingleListenrFocusNode extends FocusNode { VoidCallback? _listener; - void setSingleListener(VoidCallback listener) { + void setListener(VoidCallback listener) { if (_listener != null) { removeListener(_listener!); } @@ -101,7 +100,7 @@ class CellSingleFocusNode extends FocusNode { super.addListener(listener); } - void removeSingleListener() { + void removeAllListener() { if (_listener != null) { removeListener(_listener!); } @@ -163,7 +162,7 @@ class CellContainer extends StatelessWidget { return GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => child.requestFocus.notify(), + onTap: () => child.beginFocus.notify(), child: Container( constraints: BoxConstraints(maxWidth: width, minHeight: 46), decoration: _makeBoxDecoration(context, isFocus), 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 b2493d55ed..574063a91e 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 @@ -24,7 +24,7 @@ class _CheckboxCellState extends State { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const CheckboxCellEvent.initial()); - _listenCellRequestFocus(); + _handleRequestFocus(); super.initState(); } @@ -51,19 +51,19 @@ class _CheckboxCellState extends State { @override void didUpdateWidget(covariant CheckboxCell oldWidget) { - _listenCellRequestFocus(); + _handleRequestFocus(); super.didUpdateWidget(oldWidget); } @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.beginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } - void _listenCellRequestFocus() { - widget.requestFocus.addListener(() { + void _handleRequestFocus() { + widget.beginFocus.setListener(() { _cellBloc.add(const CheckboxCellEvent.select()); }); } 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 d11229c4cb..5c3947f05f 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 @@ -22,7 +22,7 @@ class NumberCell extends StatefulWidget with GridCellWidget { class _NumberCellState extends State { late NumberCellBloc _cellBloc; late TextEditingController _controller; - late CellSingleFocusNode _focusNode; + late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -30,14 +30,14 @@ class _NumberCellState extends State { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: contentFromState(_cellBloc.state)); - _focusNode = CellSingleFocusNode(); - _listenFocusNode(); + _focusNode = SingleListenrFocusNode(); + _listenOnFocusNodeChanged(); super.initState(); } @override Widget build(BuildContext context) { - _listenCellRequestFocus(context); + _handleCellRequestFocus(context); return BlocProvider.value( value: _cellBloc, child: MultiBlocListener( @@ -65,19 +65,17 @@ class _NumberCellState extends State { @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.beginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeSingleListener(); + _focusNode.removeAllListener(); _focusNode.dispose(); super.dispose(); } @override void didUpdateWidget(covariant NumberCell oldWidget) { - if (oldWidget != widget) { - _listenFocusNode(); - } + _listenOnFocusNodeChanged(); super.didUpdateWidget(oldWidget); } @@ -92,16 +90,16 @@ class _NumberCellState extends State { } } - void _listenFocusNode() { + void _listenOnFocusNodeChanged() { widget.onFocus.value = _focusNode.hasFocus; - _focusNode.setSingleListener(() { + _focusNode.setListener(() { widget.onFocus.value = _focusNode.hasFocus; focusChanged(); }); } - void _listenCellRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { + void _handleCellRequestFocus(BuildContext context) { + widget.beginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } 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 1563d41d86..3a79859372 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 @@ -35,7 +35,7 @@ class GridTextCell extends StatefulWidget with GridCellWidget { class _GridTextCellState extends State { late TextCellBloc _cellBloc; late TextEditingController _controller; - late CellSingleFocusNode _focusNode; + late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -44,9 +44,9 @@ class _GridTextCellState extends State { _cellBloc = getIt(param1: cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - _focusNode = CellSingleFocusNode(); + _focusNode = SingleListenrFocusNode(); - _listenFocusNode(); + _listenOnFocusNodeChanged(); _listenRequestFocus(context); super.initState(); } @@ -81,10 +81,10 @@ class _GridTextCellState extends State { @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.beginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeSingleListener(); + _focusNode.removeAllListener(); _focusNode.dispose(); super.dispose(); @@ -93,21 +93,21 @@ class _GridTextCellState extends State { @override void didUpdateWidget(covariant GridTextCell oldWidget) { if (oldWidget != widget) { - _listenFocusNode(); + _listenOnFocusNodeChanged(); } super.didUpdateWidget(oldWidget); } - void _listenFocusNode() { + void _listenOnFocusNodeChanged() { widget.onFocus.value = _focusNode.hasFocus; - _focusNode.setSingleListener(() { + _focusNode.setListener(() { widget.onFocus.value = _focusNode.hasFocus; focusChanged(); }); } void _listenRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { + widget.beginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index fe708c710a..91624c9dd1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; -import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -53,7 +51,7 @@ class _GridURLCellState extends State { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; _cellBloc = URLCellBloc(cellContext: cellContext); _cellBloc.add(const URLCellEvent.initial()); - _listenRequestFocus(context); + _handleRequestFocus(); super.initState(); } @@ -85,11 +83,17 @@ class _GridURLCellState extends State { @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.beginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } + @override + void didUpdateWidget(covariant GridURLCell oldWidget) { + _handleRequestFocus(); + super.didUpdateWidget(oldWidget); + } + TapGestureRecognizer _tapGesture(BuildContext context) { final gesture = TapGestureRecognizer(); gesture.onTap = () async { @@ -109,8 +113,8 @@ class _GridURLCellState extends State { } } - void _listenRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { + void _handleRequestFocus() { + widget.beginFocus.setListener(() { _openUrlOrEdit(_cellBloc.state.url); }); } From 9c5081bc07a054a11c4711024bc58be7e791f3c5 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 31 May 2022 21:53:04 +0800 Subject: [PATCH 50/82] chore: enable the richtext widget as large as parent --- .../src/widgets/cell/url_cell/url_cell.dart | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 91624c9dd1..9570d1219f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -71,11 +71,18 @@ class _GridURLCellState extends State { fontSize: 14, decoration: TextDecoration.underline, ), - recognizer: _tapGesture(context), ), ); - return Align(alignment: Alignment.centerLeft, child: richText); + return SizedBox.expand( + child: GestureDetector( + child: Align(alignment: Alignment.centerLeft, child: richText), + onTap: () async { + widget.onFocus.value = true; + final url = context.read().state.url; + await _openUrlOrEdit(url); + }, + )); }, ), ); @@ -94,15 +101,6 @@ class _GridURLCellState extends State { super.didUpdateWidget(oldWidget); } - TapGestureRecognizer _tapGesture(BuildContext context) { - final gesture = TapGestureRecognizer(); - gesture.onTap = () async { - final url = context.read().state.url; - await _openUrlOrEdit(url); - }; - return gesture; - } - Future _openUrlOrEdit(String url) async { final uri = Uri.parse(url); if (url.isNotEmpty && await canLaunchUrl(uri)) { From 47161153881bce83fbd3e525df4d6ff587c1c37d Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Tue, 31 May 2022 17:36:15 +0200 Subject: [PATCH 51/82] fix: convert function to camel case --- .../app_flowy/lib/workspace/application/doc/share_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart index 4e17619b9f..e24411fdc0 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_bloc.dart @@ -40,14 +40,14 @@ class DocShareBloc extends Bloc { return value; } - Future get _ExportDir async { + Future get _exportDir async { Directory documentsDir = await appFlowyDocumentDirectory(); return documentsDir; } Future get _localPath async { - final dir = await _ExportDir; + final dir = await _exportDir; return dir.path; } From 9518e164b577c060db74bc26d2860c9304bc9330 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 14:40:05 +0800 Subject: [PATCH 52/82] chore: config cell accessory --- .../grid/src/widgets/cell/cell_accessory.dart | 161 ++++++++++++++++++ .../grid/src/widgets/cell/cell_builder.dart | 64 +++---- .../grid/src/widgets/cell/checkbox_cell.dart | 4 +- .../src/widgets/cell/date_cell/date_cell.dart | 4 +- .../grid/src/widgets/cell/number_cell.dart | 8 +- .../select_option_cell.dart | 4 +- .../grid/src/widgets/cell/text_cell.dart | 8 +- .../widgets/cell/url_cell/cell_editor.dart | 8 +- .../src/widgets/cell/url_cell/url_cell.dart | 67 ++++++-- .../grid/src/widgets/row/grid_row.dart | 18 +- .../grid/src/widgets/row/row_detail.dart | 18 +- .../lib/style_widget/hover.dart | 120 ------------- 12 files changed, 284 insertions(+), 200 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart new file mode 100644 index 0000000000..d5cca704fe --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart @@ -0,0 +1,161 @@ +import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flowy_infra/size.dart'; +import 'package:styled_widget/styled_widget.dart'; + +abstract class GridCellAccessory implements Widget { + void onTap(BuildContext context); +} + +abstract class AccessoryHoverChild extends Widget { + const AccessoryHoverChild({Key? key}) : super(key: key); + + // The hover will show if the onFocus's value is true + ValueNotifier? get isFocus; + + List accessories(); +} + +class AccessoryHover extends StatefulWidget { + final AccessoryHoverChild child; + final EdgeInsets contentPadding; + const AccessoryHover({ + required this.child, + this.contentPadding = EdgeInsets.zero, + Key? key, + }) : super(key: key); + + @override + State createState() => _AccessoryHoverState(); +} + +class _AccessoryHoverState extends State { + late AccessoryHoverState _hoverState; + VoidCallback? _listenerFn; + + @override + void initState() { + _hoverState = AccessoryHoverState(); + _listenerFn = () => _hoverState.isFocus = widget.child.isFocus?.value ?? false; + widget.child.isFocus?.addListener(_listenerFn!); + + super.initState(); + } + + @override + void dispose() { + _hoverState.dispose(); + + if (_listenerFn != null) { + widget.child.isFocus?.removeListener(_listenerFn!); + _listenerFn = null; + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List children = [ + const _Background(), + Padding(padding: widget.contentPadding, child: widget.child), + ]; + final accessories = widget.child.accessories(); + if (accessories.isNotEmpty) { + children.add( + Padding( + padding: const EdgeInsets.only(right: 6), + child: AccessoryContainer(accessories: accessories), + ).positioned(right: 0), + ); + } + + return ChangeNotifierProvider.value( + value: _hoverState, + child: MouseRegion( + cursor: SystemMouseCursors.click, + opaque: false, + onEnter: (p) => setState(() => _hoverState.onHover = true), + onExit: (p) => setState(() => _hoverState.onHover = false), + child: Stack( + fit: StackFit.loose, + alignment: AlignmentDirectional.center, + children: children, + ), + ), + ); + } +} + +class AccessoryHoverState extends ChangeNotifier { + bool _onHover = false; + bool _isFocus = false; + + set onHover(bool value) { + if (_onHover != value) { + _onHover = value; + notifyListeners(); + } + } + + bool get onHover => _onHover; + + set isFocus(bool value) { + if (_isFocus != value) { + _isFocus = value; + notifyListeners(); + } + } + + bool get isFocus => _isFocus; +} + +class _Background extends StatelessWidget { + const _Background({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return Consumer( + builder: (context, state, child) { + if (state.onHover || state.isFocus) { + return FlowyHoverContainer( + style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), + ); + } else { + return const SizedBox(); + } + }, + ); + } +} + +class AccessoryContainer extends StatelessWidget { + final List accessories; + const AccessoryContainer({required this.accessories, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final children = accessories.map((accessory) { + final hover = FlowyHover( + style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface), + builder: (_, onHover) => Container( + width: 26, + height: 26, + padding: const EdgeInsets.all(3), + child: accessory, + ), + ); + return GestureDetector( + child: hover, + behavior: HitTestBehavior.opaque, + onTap: () => accessory.onTap(context), + ); + }).toList(); + + return Wrap(children: children, spacing: 6); + } +} 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 0fb342bf03..d97ed086b4 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 @@ -1,5 +1,4 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; import 'package:flutter/widgets.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart'; @@ -8,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'cell_accessory.dart'; import 'checkbox_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; @@ -48,22 +48,20 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellExpander implements Widget { - void onExpand(BuildContext context); -} - -abstract class GridCellWidget implements FlowyHoverWidget { +abstract class GridCellWidget implements AccessoryHoverChild, CellContainerFocustable { @override - final ValueNotifier onFocus = ValueNotifier(false); + final ValueNotifier isFocus = ValueNotifier(false); - final GridCellBeginFocusFocus beginFocus = GridCellBeginFocusFocus(); - - GridCellExpander? buildExpander() { - return null; + @override + List accessories() { + return List.empty(); } + + @override + final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus(); } -class GridCellBeginFocusFocus extends ChangeNotifier { +class GridCellRequestBeginFocus extends ChangeNotifier { VoidCallback? _listener; void setListener(VoidCallback listener) { @@ -130,9 +128,14 @@ class CellStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } +abstract class CellContainerFocustable { + // Listen on the requestBeginFocus if the + GridCellRequestBeginFocus get requestBeginFocus; +} + class CellContainer extends StatelessWidget { final GridCellWidget child; - final GridCellExpander? expander; + final List accessories; final double width; final RegionStateNotifier rowStateNotifier; const CellContainer({ @@ -140,7 +143,7 @@ class CellContainer extends StatelessWidget { required this.child, required this.width, required this.rowStateNotifier, - this.expander, + this.accessories = const [], }) : super(key: key); @override @@ -152,17 +155,17 @@ class CellContainer extends StatelessWidget { selector: (context, notifier) => notifier.isFocus, builder: (context, isFocus, _) { Widget container = Center(child: child); - child.onFocus.addListener(() { - Provider.of(context, listen: false).isFocus = child.onFocus.value; + child.isFocus.addListener(() { + Provider.of(context, listen: false).isFocus = child.isFocus.value; }); - if (expander != null) { - container = CellEnterRegion(child: container, expander: expander!); + if (accessories.isNotEmpty) { + container = CellEnterRegion(child: container, accessories: accessories); } return GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => child.beginFocus.notify(), + onTap: () => child.requestBeginFocus.notify(), child: Container( constraints: BoxConstraints(maxWidth: width, minHeight: 46), decoration: _makeBoxDecoration(context, isFocus), @@ -189,33 +192,17 @@ class CellContainer extends StatelessWidget { class CellEnterRegion extends StatelessWidget { final Widget child; - final GridCellExpander expander; - const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key); + final List accessories; + const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final theme = context.watch(); - return Selector( selector: (context, notifier) => notifier.onEnter, builder: (context, onEnter, _) { List children = [child]; if (onEnter) { - final hover = FlowyHover( - style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface), - builder: (_, onHover) => Container( - width: 26, - height: 26, - padding: const EdgeInsets.all(3), - child: expander, - ), - ); - - children.add(GestureDetector( - child: hover, - behavior: HitTestBehavior.opaque, - onTap: () => expander.onExpand(context), - ).positioned(right: 0)); + children.add(AccessoryContainer(accessories: accessories).positioned(right: 0)); } return MouseRegion( @@ -225,7 +212,6 @@ class CellEnterRegion extends StatelessWidget { child: Stack( alignment: AlignmentDirectional.center, fit: StackFit.expand, - // alignment: AlignmentDirectional.centerEnd, children: children, ), ); 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 574063a91e..78910348b3 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 @@ -57,13 +57,13 @@ class _CheckboxCellState extends State { @override Future dispose() async { - widget.beginFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } void _handleRequestFocus() { - widget.beginFocus.setListener(() { + widget.requestBeginFocus.setListener(() { _cellBloc.add(const CheckboxCellEvent.select()); }); } 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 417c9f270e..c644ac61a5 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 @@ -76,8 +76,8 @@ class _DateCellState extends State { void _showCalendar(BuildContext context) { final bloc = context.read(); - widget.onFocus.value = true; - final calendar = DateCellEditor(onDismissed: () => widget.onFocus.value = false); + widget.isFocus.value = true; + final calendar = DateCellEditor(onDismissed: () => widget.isFocus.value = false); calendar.show( context, cellContext: bloc.cellContext.clone(), 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 5c3947f05f..64fff54c47 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 @@ -65,7 +65,7 @@ class _NumberCellState extends State { @override Future dispose() async { - widget.beginFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); _focusNode.removeAllListener(); @@ -91,15 +91,15 @@ class _NumberCellState extends State { } void _listenOnFocusNodeChanged() { - widget.onFocus.value = _focusNode.hasFocus; + widget.isFocus.value = _focusNode.hasFocus; _focusNode.setListener(() { - widget.onFocus.value = _focusNode.hasFocus; + widget.isFocus.value = _focusNode.hasFocus; focusChanged(); }); } void _handleCellRequestFocus(BuildContext context) { - widget.beginFocus.setListener(() { + widget.requestBeginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } 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 c57a1865be..aa780263b5 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 @@ -59,7 +59,7 @@ class _SingleSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.onFocus.value = value, + onFocus: (value) => widget.isFocus.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), @@ -113,7 +113,7 @@ class _MultiSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.onFocus.value = value, + onFocus: (value) => widget.isFocus.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), 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 3a79859372..b2eda288a1 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 @@ -81,7 +81,7 @@ class _GridTextCellState extends State { @override Future dispose() async { - widget.beginFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); _focusNode.removeAllListener(); @@ -99,15 +99,15 @@ class _GridTextCellState extends State { } void _listenOnFocusNodeChanged() { - widget.onFocus.value = _focusNode.hasFocus; + widget.isFocus.value = _focusNode.hasFocus; _focusNode.setListener(() { - widget.onFocus.value = _focusNode.hasFocus; + widget.isFocus.value = _focusNode.hasFocus; focusChanged(); }); } void _listenRequestFocus(BuildContext context) { - widget.beginFocus.setListener(() { + widget.requestBeginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart index c16e18906f..15af124749 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; -class URLCellEditor extends StatefulWidget { +class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { final GridURLCellContext cellContext; const URLCellEditor({required this.cellContext, Key? key}) : super(key: key); @@ -34,12 +34,18 @@ class URLCellEditor extends StatefulWidget { identifier: URLCellEditor.identifier(), anchorContext: context, anchorDirection: AnchorDirection.bottomWithCenterAligned, + delegate: editor, ); } static String identifier() { return (URLCellEditor).toString(); } + + @override + bool asBarrier() { + return true; + } } class _URLCellEditorState extends State { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 9570d1219f..8ba089b6a5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -1,9 +1,10 @@ import 'dart:async'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -13,11 +14,19 @@ import 'cell_editor.dart'; class GridURLCellStyle extends GridCellStyle { String? placeholder; + List accessoryTypes; + GridURLCellStyle({ this.placeholder, + this.accessoryTypes = const [], }); } +enum GridURLCellAccessoryType { + edit, + copyURL, +} + class GridURLCell extends StatefulWidget with GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final GridURLCellStyle? cellStyle; @@ -37,9 +46,30 @@ class GridURLCell extends StatefulWidget with GridCellWidget { State createState() => _GridURLCellState(); @override - GridCellExpander? buildExpander() { - final cellContext = cellContextBuilder.build() as GridURLCellContext; - return _EditURLCellIndicator(cellContext: cellContext); + List accessories() { + final List accessories = []; + if (cellStyle != null) { + accessories.addAll(cellStyle!.accessoryTypes.map(accessoryFromType)); + } + + // If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit + if (accessories.isEmpty) { + accessories.add(accessoryFromType(GridURLCellAccessoryType.edit)); + } + + return accessories; + } + + GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty) { + switch (ty) { + case GridURLCellAccessoryType.edit: + final cellContext = cellContextBuilder.build() as GridURLCellContext; + return _EditURLAccessory(cellContext: cellContext); + + case GridURLCellAccessoryType.copyURL: + final cellContext = cellContextBuilder.build() as GridURLCellContext; + return _CopyURLAccessory(cellContext: cellContext); + } } } @@ -78,7 +108,7 @@ class _GridURLCellState extends State { child: GestureDetector( child: Align(alignment: Alignment.centerLeft, child: richText), onTap: () async { - widget.onFocus.value = true; + widget.isFocus.value = true; final url = context.read().state.url; await _openUrlOrEdit(url); }, @@ -90,7 +120,7 @@ class _GridURLCellState extends State { @override Future dispose() async { - widget.beginFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } @@ -112,15 +142,15 @@ class _GridURLCellState extends State { } void _handleRequestFocus() { - widget.beginFocus.setListener(() { + widget.requestBeginFocus.setListener(() { _openUrlOrEdit(_cellBloc.state.url); }); } } -class _EditURLCellIndicator extends StatelessWidget with GridCellExpander { +class _EditURLAccessory extends StatelessWidget with GridCellAccessory { final GridURLCellContext cellContext; - const _EditURLCellIndicator({required this.cellContext, Key? key}) : super(key: key); + const _EditURLAccessory({required this.cellContext, Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -129,7 +159,24 @@ class _EditURLCellIndicator extends StatelessWidget with GridCellExpander { } @override - void onExpand(BuildContext context) { + void onTap(BuildContext context) { URLCellEditor.show(context, cellContext); } } + +class _CopyURLAccessory extends StatelessWidget with GridCellAccessory { + final GridURLCellContext cellContext; + const _CopyURLAccessory({required this.cellContext, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return svgWidget("editor/copy", color: theme.iconColor); + } + + @override + void onTap(BuildContext context) { + final content = cellContext.getCellData()?.content ?? ""; + Clipboard.setData(ClipboardData(text: content)); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index 32e0ea2663..171c6d3d12 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -171,16 +172,17 @@ class _RowCells extends StatelessWidget { return gridCellMap.values.map( (gridCell) { final GridCellWidget child = buildGridCellWidget(gridCell, cellCache); - GridCellExpander? expander = child.buildExpander(); + List accessories = []; if (gridCell.field.isPrimary) { - expander = _PrimaryCellExpander(onTap: onExpand); + accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); } + accessories.addAll(child.accessories()); return CellContainer( width: gridCell.field.width.toDouble(), child: child, rowStateNotifier: Provider.of(context, listen: false), - expander: expander, + accessories: accessories, ); }, ).toList(); @@ -200,9 +202,9 @@ class RegionStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } -class _PrimaryCellExpander extends StatelessWidget with GridCellExpander { - final VoidCallback onTap; - const _PrimaryCellExpander({required this.onTap, Key? key}) : super(key: key); +class _PrimaryCellAccessory extends StatelessWidget with GridCellAccessory { + final VoidCallback onTapCallback; + const _PrimaryCellAccessory({required this.onTapCallback, Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -211,8 +213,8 @@ class _PrimaryCellExpander extends StatelessWidget with GridCellExpander { } @override - void onExpand(BuildContext context) { - onTap(); + void onTap(BuildContext context) { + onTapCallback(); } } 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 0900039b1f..87fc0594c4 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 @@ -3,6 +3,7 @@ import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart'; @@ -149,12 +150,9 @@ class _RowDetailCell extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); + final style = _customCellStyle(theme, gridCell.field.fieldType); + final cell = buildGridCellWidget(gridCell, cellCache, style: style); - final cell = buildGridCellWidget( - gridCell, - cellCache, - style: _buildCellStyle(theme, gridCell.field.fieldType), - ); return ConstrainedBox( constraints: const BoxConstraints(minHeight: 40), child: IntrinsicHeight( @@ -168,7 +166,7 @@ class _RowDetailCell extends StatelessWidget { ), const HSpace(10), Expanded( - child: FlowyHover2( + child: AccessoryHover( child: cell, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), ), @@ -191,7 +189,7 @@ class _RowDetailCell extends StatelessWidget { } } -GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { +GridCellStyle? _customCellStyle(AppTheme theme, FieldType fieldType) { switch (fieldType) { case FieldType.Checkbox: return null; @@ -217,7 +215,11 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { case FieldType.URL: return GridURLCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + accessoryTypes: [ + GridURLCellAccessoryType.edit, + GridURLCellAccessoryType.copyURL, + ], ); } - return null; + throw UnimplementedError; } 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 5a6ef79470..42ee43cabc 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 @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; // ignore: unused_import import 'package:flowy_infra/time/duration.dart'; -import 'package:flowy_infra/size.dart'; -import 'package:flowy_infra/theme.dart'; -import 'package:provider/provider.dart'; typedef HoverBuilder = Widget Function(BuildContext context, bool onHover); @@ -102,120 +99,3 @@ class FlowyHoverContainer extends StatelessWidget { ); } } - -// -abstract class FlowyHoverWidget extends Widget { - const FlowyHoverWidget({Key? key}) : super(key: key); - - ValueNotifier? get onFocus; -} - -class FlowyHover2 extends StatefulWidget { - final FlowyHoverWidget child; - final EdgeInsets contentPadding; - const FlowyHover2({ - required this.child, - this.contentPadding = EdgeInsets.zero, - Key? key, - }) : super(key: key); - - @override - State createState() => _FlowyHover2State(); -} - -class _FlowyHover2State extends State { - late FlowyHoverState _hoverState; - VoidCallback? _listenerFn; - - @override - void initState() { - _hoverState = FlowyHoverState(); - - 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(); - } - - @override - Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _hoverState, - child: MouseRegion( - cursor: SystemMouseCursors.click, - opaque: false, - onEnter: (p) => setState(() => _hoverState.onHover = true), - onExit: (p) => setState(() => _hoverState.onHover = false), - child: Stack( - fit: StackFit.loose, - alignment: AlignmentDirectional.center, - children: [ - const _HoverBackground(), - Padding( - padding: widget.contentPadding, - child: widget.child, - ), - ], - ), - ), - ); - } -} - -class _HoverBackground extends StatelessWidget { - const _HoverBackground({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final theme = context.watch(); - return Consumer( - builder: (context, state, child) { - if (state.onHover || state.onFocus) { - return FlowyHoverContainer( - style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), - ); - } else { - return const SizedBox(); - } - }, - ); - } -} - -class FlowyHoverState extends ChangeNotifier { - bool _onHover = false; - bool _onFocus = false; - - set onHover(bool value) { - if (_onHover != value) { - _onHover = value; - notifyListeners(); - } - } - - bool get onHover => _onHover; - - set onFocus(bool value) { - if (_onFocus != value) { - _onFocus = value; - notifyListeners(); - } - } - - bool get onFocus => _onFocus; -} From 10365217caea308a542e721afa15d7f8a63ea37c Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 15:22:18 +0800 Subject: [PATCH 53/82] chore: copy property to pasteboard --- .../app_flowy/assets/translations/en.json | 3 +- .../app_flowy/lib/startup/deps_resolver.dart | 3 + .../lib/startup/tasks/app_widget.dart | 68 ++++++++++--------- .../cell/cell_service/context_builder.dart | 10 +-- .../presentation/home/home_stack.dart | 9 +-- .../src/widgets/cell/url_cell/url_cell.dart | 7 +- .../grid/src/widgets/row/row_detail.dart | 1 - .../widgets/float_bubble/question_bubble.dart | 4 +- frontend/app_flowy/pubspec.yaml | 2 +- 9 files changed, 56 insertions(+), 51 deletions(-) diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 4e6c8f3420..e465e79607 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -180,7 +180,8 @@ "row": { "duplicate": "Duplicate", "delete": "Delete", - "textPlaceholder": "Empty" + "textPlaceholder": "Empty", + "copyProperty": "Copied property to clipboard" }, "selectOption": { "create": "Create", diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 9be9420fb5..b378d6ee7b 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -16,6 +16,7 @@ import 'package:app_flowy/workspace/presentation/home/menu/menu.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:get_it/get_it.dart'; class DependencyResolver { @@ -46,6 +47,8 @@ void _resolveUserDeps(GetIt getIt) { } void _resolveHomeDeps(GetIt getIt) { + getIt.registerSingleton(FToast()); + getIt.registerSingleton(MenuSharedState()); getIt.registerFactoryParam( diff --git a/frontend/app_flowy/lib/startup/tasks/app_widget.dart b/frontend/app_flowy/lib/startup/tasks/app_widget.dart index 1747cfd8ec..9961142c6d 100644 --- a/frontend/app_flowy/lib/startup/tasks/app_widget.dart +++ b/frontend/app_flowy/lib/startup/tasks/app_widget.dart @@ -67,40 +67,42 @@ class ApplicationWidget extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => ChangeNotifierProvider.value( - value: settingModel, - builder: (context, _) { - const ratio = 1.73; - const minWidth = 600.0; - setWindowMinSize(const Size(minWidth, minWidth / ratio)); - settingModel.readLocaleWhenAppLaunch(context); - AppTheme theme = context.select( - (value) => value.theme, - ); - Locale locale = context.select( - (value) => value.locale, - ); + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: settingModel, + builder: (context, _) { + const ratio = 1.73; + const minWidth = 600.0; + setWindowMinSize(const Size(minWidth, minWidth / ratio)); + settingModel.readLocaleWhenAppLaunch(context); + AppTheme theme = context.select( + (value) => value.theme, + ); + Locale locale = context.select( + (value) => value.locale, + ); - return MultiProvider( - providers: [ - Provider.value(value: theme), - Provider.value(value: locale), - ], - builder: (context, _) { - return MaterialApp( - builder: overlayManagerBuilder(), - debugShowCheckedModeBanner: false, - theme: theme.themeData, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: locale, - navigatorKey: AppGlobals.rootNavKey, - home: child, - ); - }, - ); - }, - ); + return MultiProvider( + providers: [ + Provider.value(value: theme), + Provider.value(value: locale), + ], + builder: (context, _) { + return MaterialApp( + builder: overlayManagerBuilder(), + debugShowCheckedModeBanner: false, + theme: theme.themeData, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: locale, + navigatorKey: AppGlobals.rootNavKey, + home: child, + ); + }, + ); + }, + ); + } } class AppGlobals { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index e4141c3e16..6c0fb560e8 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -105,7 +105,7 @@ class _GridCellContext extends Equatable { final FieldService _fieldService; late final CellListener _cellListener; - late final ValueNotifier _cellDataNotifier; + late final ValueNotifier? _cellDataNotifier; bool isListening = false; VoidCallback? _onFieldChangedFn; Timer? _loadDataOperation; @@ -163,19 +163,19 @@ class _GridCellContext extends Equatable { } onCellChangedFn() { - onCellChanged(_cellDataNotifier.value); + onCellChanged(_cellDataNotifier?.value); if (cellDataLoader.config.reloadOnCellChanged) { _loadData(); } } - _cellDataNotifier.addListener(onCellChangedFn); + _cellDataNotifier?.addListener(onCellChangedFn); return onCellChangedFn; } void removeListener(VoidCallback fn) { - _cellDataNotifier.removeListener(fn); + _cellDataNotifier?.removeListener(fn); } T? getCellData({bool loadIfNoCache = true}) { @@ -211,7 +211,7 @@ class _GridCellContext extends Equatable { _loadDataOperation?.cancel(); _loadDataOperation = Timer(const Duration(milliseconds: 10), () { cellDataLoader.loadData().then((data) { - _cellDataNotifier.value = data; + _cellDataNotifier?.value = data; cellCache.insert(GridCellCacheData(key: _cacheKey, object: data)); }); }); diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart index c16c965a82..c9c210ab35 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart @@ -2,15 +2,13 @@ import 'dart:io' show Platform; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/home/home_bloc.dart'; -import 'package:app_flowy/workspace/presentation/home/home_screen.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:time/time.dart'; -import 'package:fluttertoast/fluttertoast.dart'; - import 'package:app_flowy/plugin/plugin.dart'; import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart'; import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; @@ -22,8 +20,6 @@ import 'package:flowy_infra/notifier.dart'; typedef NavigationCallback = void Function(String id); -late FToast fToast; - class HomeStack extends StatelessWidget { static GlobalKey scaffoldKey = GlobalKey(); // final Size size; @@ -74,8 +70,7 @@ class _FadingIndexedStackState extends State { @override void initState() { super.initState(); - fToast = FToast(); - fToast.init(HomeScreen.scaffoldKey.currentState!.context); + initToastWithContext(context); } @override diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 8ba089b6a5..e823aedbfc 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -1,6 +1,9 @@ import 'dart:async'; +import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; @@ -176,7 +179,9 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory { @override void onTap(BuildContext context) { - final content = cellContext.getCellData()?.content ?? ""; + final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? ""; Clipboard.setData(ClipboardData(text: content)); + + showMessageToast(LocaleKeys.grid_row_copyProperty.tr()); } } 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 87fc0594c4..029f3f61ea 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 @@ -11,7 +11,6 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index 762a978693..8fecbc9489 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -1,5 +1,5 @@ +import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/tasks/rust_sdk.dart'; -import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme.dart'; @@ -115,7 +115,7 @@ class _DebugToast extends StatelessWidget { } void show() { - fToast.showToast( + getIt().showToast( child: this, gravity: ToastGravity.BOTTOM, toastDuration: const Duration(seconds: 3), diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index eb7b17dc21..8dbc3b4c70 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -72,7 +72,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 device_info_plus: ^3.2.1 - fluttertoast: ^8.0.8 + fluttertoast: ^8.0.9 table_calendar: ^3.0.5 reorderables: ^0.5.0 linked_scroll_controller: ^0.2.0 From 57e5c36fe8af79f7bf7ce7e5d9f917039aa86b10 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 15:28:54 +0800 Subject: [PATCH 54/82] chore: config toast --- .../workspace/presentation/home/toast.dart | 37 ++++++++++++ .../widgets/float_bubble/question_bubble.dart | 60 +++---------------- 2 files changed, 46 insertions(+), 51 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/presentation/home/toast.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/home/toast.dart b/frontend/app_flowy/lib/workspace/presentation/home/toast.dart new file mode 100644 index 0000000000..28241c0ec4 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/home/toast.dart @@ -0,0 +1,37 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class FlowyMessageToast extends StatelessWidget { + final String message; + const FlowyMessageToast({required this.message, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + child: FlowyText.medium(message, color: Colors.white), + ), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(4)), + color: Colors.black, + ), + ); + } +} + +void initToastWithContext(BuildContext context) { + getIt().init(context); +} + +void showMessageToast(String message) { + final child = FlowyMessageToast(message: message); + + getIt().showToast( + child: child, + gravity: ToastGravity.BOTTOM, + toastDuration: const Duration(seconds: 3), + ); +} diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index 8fecbc9489..6cc150489c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -1,5 +1,5 @@ -import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/tasks/rust_sdk.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme.dart'; @@ -16,7 +16,6 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:fluttertoast/fluttertoast.dart'; class QuestionBubble extends StatelessWidget { const QuestionBubble({Key? key}) : super(key: key); @@ -46,7 +45,7 @@ class QuestionBubble extends StatelessWidget { _launchURL("https://discord.gg/9Q2xaN37tV"); break; case BubbleAction.debug: - const _DebugToast().show(); + _DebugToast().show(); break; } }); @@ -71,55 +70,14 @@ class QuestionBubble extends StatelessWidget { } } -class _DebugToast extends StatelessWidget { - const _DebugToast({Key? key}) : super(key: key); +class _DebugToast { + void show() async { + var debugInfo = ""; + debugInfo += await _getDeviceInfo(); + debugInfo += await _getDocumentPath(); + Clipboard.setData(ClipboardData(text: debugInfo)); - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: Future(() async { - var debugInfo = ""; - debugInfo += await _getDeviceInfo(); - debugInfo += await _getDocumentPath(); - - Clipboard.setData(ClipboardData(text: debugInfo)); - }), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return _done(context, Text("Error: ${snapshot.error}")); - } else { - return _done(context, null); - } - } else { - return const CircularProgressIndicator(); - } - }, - ); - } - - Widget _done(BuildContext context, Widget? error) { - final theme = context.watch(); - return Container( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), - decoration: BoxDecoration(borderRadius: BorderRadius.circular(25.0), color: theme.main1), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.check), - const SizedBox(width: 12.0), - (error == null) ? Text(LocaleKeys.questionBubble_debug_success.tr()) : error - ], - ), - ); - } - - void show() { - getIt().showToast( - child: this, - gravity: ToastGravity.BOTTOM, - toastDuration: const Duration(seconds: 3), - ); + showMessageToast(LocaleKeys.questionBubble_debug_success.tr()); } Future _getDeviceInfo() async { From 5bd0fb9fae102e476c4650025b2b0a6a09f4621b Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 16:24:23 +0800 Subject: [PATCH 55/82] chore: config build context of URLCellEditor --- .../grid/src/widgets/cell/cell_accessory.dart | 28 +++++++---- .../grid/src/widgets/cell/cell_builder.dart | 18 ++++--- .../src/widgets/cell/url_cell/url_cell.dart | 50 +++++++++++-------- .../grid/src/widgets/row/grid_row.dart | 18 +++++-- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart index d5cca704fe..41e22b8b98 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart @@ -6,21 +6,29 @@ import 'package:provider/provider.dart'; import 'package:flowy_infra/size.dart'; import 'package:styled_widget/styled_widget.dart'; -abstract class GridCellAccessory implements Widget { - void onTap(BuildContext context); +class GridCellAccessoryBuildContext { + final BuildContext anchorContext; + + GridCellAccessoryBuildContext({required this.anchorContext}); } -abstract class AccessoryHoverChild extends Widget { - const AccessoryHoverChild({Key? key}) : super(key: key); +abstract class GridCellAccessory implements Widget { + void onTap(); +} + +typedef AccessoryBuilder = List Function(GridCellAccessoryBuildContext buildContext); + +abstract class AccessoryWidget extends Widget { + const AccessoryWidget({Key? key}) : super(key: key); // The hover will show if the onFocus's value is true ValueNotifier? get isFocus; - List accessories(); + AccessoryBuilder? get accessoryBuilder; } class AccessoryHover extends StatefulWidget { - final AccessoryHoverChild child; + final AccessoryWidget child; final EdgeInsets contentPadding; const AccessoryHover({ required this.child, @@ -62,8 +70,10 @@ class _AccessoryHoverState extends State { const _Background(), Padding(padding: widget.contentPadding, child: widget.child), ]; - final accessories = widget.child.accessories(); - if (accessories.isNotEmpty) { + + final accessoryBuilder = widget.child.accessoryBuilder; + if (accessoryBuilder != null) { + final accessories = accessoryBuilder((GridCellAccessoryBuildContext(anchorContext: context))); children.add( Padding( padding: const EdgeInsets.only(right: 6), @@ -152,7 +162,7 @@ class AccessoryContainer extends StatelessWidget { return GestureDetector( child: hover, behavior: HitTestBehavior.opaque, - onTap: () => accessory.onTap(context), + onTap: () => accessory.onTap(), ); }).toList(); 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 d97ed086b4..65bff03ecf 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 @@ -48,14 +48,12 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget implements AccessoryHoverChild, CellContainerFocustable { +abstract class GridCellWidget implements AccessoryWidget, CellContainerFocustable { @override final ValueNotifier isFocus = ValueNotifier(false); @override - List accessories() { - return List.empty(); - } + List Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null; @override final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus(); @@ -135,7 +133,7 @@ abstract class CellContainerFocustable { class CellContainer extends StatelessWidget { final GridCellWidget child; - final List accessories; + final AccessoryBuilder? accessoryBuilder; final double width; final RegionStateNotifier rowStateNotifier; const CellContainer({ @@ -143,7 +141,7 @@ class CellContainer extends StatelessWidget { required this.child, required this.width, required this.rowStateNotifier, - this.accessories = const [], + this.accessoryBuilder, }) : super(key: key); @override @@ -159,8 +157,12 @@ class CellContainer extends StatelessWidget { Provider.of(context, listen: false).isFocus = child.isFocus.value; }); - if (accessories.isNotEmpty) { - container = CellEnterRegion(child: container, accessories: accessories); + if (accessoryBuilder != null) { + final buildContext = GridCellAccessoryBuildContext(anchorContext: context); + final accessories = accessoryBuilder!(buildContext); + if (accessories.isNotEmpty) { + container = CellEnterRegion(child: container, accessories: accessories); + } } return GestureDetector( diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index e823aedbfc..56f0b421a5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -48,32 +48,34 @@ class GridURLCell extends StatefulWidget with GridCellWidget { @override State createState() => _GridURLCellState(); - @override - List accessories() { - final List accessories = []; - if (cellStyle != null) { - accessories.addAll(cellStyle!.accessoryTypes.map(accessoryFromType)); - } - - // If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit - if (accessories.isEmpty) { - accessories.add(accessoryFromType(GridURLCellAccessoryType.edit)); - } - - return accessories; - } - - GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty) { + GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) { switch (ty) { case GridURLCellAccessoryType.edit: final cellContext = cellContextBuilder.build() as GridURLCellContext; - return _EditURLAccessory(cellContext: cellContext); + return _EditURLAccessory(cellContext: cellContext, anchorContext: buildContext.anchorContext); case GridURLCellAccessoryType.copyURL: final cellContext = cellContextBuilder.build() as GridURLCellContext; return _CopyURLAccessory(cellContext: cellContext); } } + + @override + List Function(GridCellAccessoryBuildContext buildContext) get accessoryBuilder => (buildContext) { + final List accessories = []; + if (cellStyle != null) { + accessories.addAll(cellStyle!.accessoryTypes.map((ty) { + return accessoryFromType(ty, buildContext); + })); + } + + // If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit + if (accessories.isEmpty) { + accessories.add(accessoryFromType(GridURLCellAccessoryType.edit, buildContext)); + } + + return accessories; + }; } class _GridURLCellState extends State { @@ -153,7 +155,12 @@ class _GridURLCellState extends State { class _EditURLAccessory extends StatelessWidget with GridCellAccessory { final GridURLCellContext cellContext; - const _EditURLAccessory({required this.cellContext, Key? key}) : super(key: key); + final BuildContext anchorContext; + const _EditURLAccessory({ + required this.cellContext, + required this.anchorContext, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -162,8 +169,8 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory { } @override - void onTap(BuildContext context) { - URLCellEditor.show(context, cellContext); + void onTap() { + URLCellEditor.show(anchorContext, cellContext); } } @@ -178,10 +185,9 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory { } @override - void onTap(BuildContext context) { + void onTap() { final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? ""; Clipboard.setData(ClipboardData(text: content)); - showMessageToast(LocaleKeys.grid_row_copyProperty.tr()); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index 171c6d3d12..b646af6c40 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -176,13 +176,25 @@ class _RowCells extends StatelessWidget { if (gridCell.field.isPrimary) { accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); } - accessories.addAll(child.accessories()); + + accessoryBuilder(buildContext) { + final builder = child.accessoryBuilder; + List accessories = []; + if (gridCell.field.isPrimary) { + accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); + } + + if (builder != null) { + accessories.addAll(builder(buildContext)); + } + return accessories; + } return CellContainer( width: gridCell.field.width.toDouble(), child: child, rowStateNotifier: Provider.of(context, listen: false), - accessories: accessories, + accessoryBuilder: accessoryBuilder, ); }, ).toList(); @@ -213,7 +225,7 @@ class _PrimaryCellAccessory extends StatelessWidget with GridCellAccessory { } @override - void onTap(BuildContext context) { + void onTap() { onTapCallback(); } } From 0b83684c660698308e9a88a6f3e5b8efc114fa57 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 16:40:24 +0800 Subject: [PATCH 56/82] chore: optimaze tap area of row detail cells --- .../grid/src/widgets/row/number_cell.dart | 44 ------------------- .../grid/src/widgets/row/row_detail.dart | 16 ++++--- 2 files changed, 10 insertions(+), 50 deletions(-) delete mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart deleted file mode 100644 index 0f3f7c5f32..0000000000 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/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 029f3f61ea..fbff4665d9 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 @@ -152,6 +152,15 @@ class _RowDetailCell extends StatelessWidget { final style = _customCellStyle(theme, gridCell.field.fieldType); final cell = buildGridCellWidget(gridCell, cellCache, style: style); + final gesture = GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => cell.requestBeginFocus.notify(), + child: AccessoryHover( + child: cell, + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), + ), + ); + return ConstrainedBox( constraints: const BoxConstraints(minHeight: 40), child: IntrinsicHeight( @@ -164,12 +173,7 @@ class _RowDetailCell extends StatelessWidget { child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), ), const HSpace(10), - Expanded( - child: AccessoryHover( - child: cell, - contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), - ), - ), + Expanded(child: gesture), ], ), ), From b286276108568d4cdc179c53b05beac624a518b9 Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 1 Jun 2022 16:44:24 +0800 Subject: [PATCH 57/82] chore: remove unavailable action of GridSettingAction and RowAction --- .../plugins/grid/src/widgets/row/row_action_sheet.dart | 1 + .../plugins/grid/src/widgets/toolbar/grid_setting.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart index 40e77d9c43..1d7886c86c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart @@ -24,6 +24,7 @@ class GridRowActionSheet extends StatelessWidget { child: BlocBuilder( builder: (context, state) { final cells = _RowAction.values + .where((value) => value.enable()) .map( (action) => _RowActionCell( action: action, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart index 05a72a5504..7e3c14e021 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart @@ -85,7 +85,7 @@ class GridSettingList extends StatelessWidget { } Widget _renderList() { - final cells = GridSettingAction.values.map((action) { + final cells = GridSettingAction.values.where((value) => value.enable()).map((action) { return _SettingItem(action: action); }).toList(); From 4b88aa868364e633ff367a1bf0e9e9a758aa8ad9 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 15:06:15 +0800 Subject: [PATCH 58/82] fix: edit date time issue --- .../app_flowy/assets/translations/en.json | 4 + .../cell/cell_service/context_builder.dart | 2 +- .../cell/cell_service/data_persistence.dart | 8 +- .../application/grid/cell/date_cal_bloc.dart | 69 +++--- .../application/grid/cell/date_cell_bloc.dart | 26 +-- .../src/widgets/cell/date_cell/date_cell.dart | 2 +- .../widgets/cell/date_cell/date_editor.dart | 12 +- .../type_options/checkbox_type_option.rs | 2 +- .../field/type_options/date_type_option.rs | 205 ++++++++++-------- .../number_type_option/number_type_option.rs | 2 +- .../type_options/selection_type_option.rs | 4 +- .../field/type_options/text_type_option.rs | 4 +- .../field/type_options/url_type_option.rs | 2 +- .../src/services/row/cell_data_operation.rs | 10 +- 14 files changed, 202 insertions(+), 150 deletions(-) diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index e465e79607..dfc80b402b 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -198,6 +198,10 @@ "colorPannelTitle": "Colors", "pannelTitle": "Select an option or create one", "searchOption": "Search for an option" + }, + "date": { + "timeHintTextInTwelveHour": "12:00 AM", + "timeHintTextInTwentyFourHour": "12:00" } } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 6c0fb560e8..4a27b10e1b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -2,7 +2,7 @@ part of 'cell_service.dart'; typedef GridCellContext = _GridCellContext; typedef GridSelectOptionCellContext = _GridCellContext; -typedef GridDateCellContext = _GridCellContext; +typedef GridDateCellContext = _GridCellContext; typedef GridURLCellContext = _GridCellContext; class GridCellContextBuilder { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart index e09a528e44..2ad217e062 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_persistence.dart @@ -31,18 +31,18 @@ class CellDataPersistence implements _GridCellDataPersistence { } @freezed -class DateCalData with _$DateCalData { - const factory DateCalData({required DateTime date, String? time}) = _DateCellPersistenceData; +class CalendarData with _$CalendarData { + const factory CalendarData({required DateTime date, String? time}) = _CalendarData; } -class DateCellDataPersistence implements _GridCellDataPersistence { +class DateCellDataPersistence implements _GridCellDataPersistence { final GridCell gridCell; DateCellDataPersistence({ required this.gridCell, }); @override - Future> save(DateCalData data) { + Future> save(CalendarData data) { var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell); final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString(); 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 15f18707f8..c72a10b481 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 @@ -38,9 +38,9 @@ class DateCalBloc extends Bloc { emit(state.copyWith(focusedDay: focusedDay)); }, didReceiveCellUpdate: (DateCellData? cellData) { - final dateData = dateDataFromCellData(cellData); - final time = dateData.foldRight("", (dateData, previous) => dateData.time); - emit(state.copyWith(dateData: dateData, time: time)); + final calData = calDataFromCellData(cellData); + final time = calData.foldRight("", (dateData, previous) => dateData.time); + emit(state.copyWith(calData: calData, time: time)); }, setIncludeTime: (includeTime) async { await _updateTypeOption(emit, includeTime: includeTime); @@ -52,7 +52,12 @@ class DateCalBloc extends Bloc { await _updateTypeOption(emit, timeFormat: timeFormat); }, setTime: (time) async { - await _updateDateData(emit, time: time); + if (state.calData.isSome()) { + await _updateDateData(emit, time: time); + } + }, + didUpdateCalData: (Option data, Option timeFormatError) { + emit(state.copyWith(calData: data, timeFormatError: timeFormatError)); }, ); }, @@ -60,8 +65,8 @@ class DateCalBloc extends Bloc { } Future _updateDateData(Emitter emit, {DateTime? date, String? time}) { - final DateCalData newDateData = state.dateData.fold( - () => DateCalData(date: date ?? DateTime.now(), time: time), + final CalendarData newDateData = state.calData.fold( + () => CalendarData(date: date ?? DateTime.now(), time: time), (dateData) { var newDateData = dateData; if (date != null && !isSameDay(newDateData.date, date)) { @@ -78,24 +83,22 @@ class DateCalBloc extends Bloc { return _saveDateData(emit, newDateData); } - Future _saveDateData(Emitter emit, DateCalData newDateData) async { - if (state.dateData == Some(newDateData)) { + Future _saveDateData(Emitter emit, CalendarData newCalData) async { + if (state.calData == Some(newCalData)) { return; } - cellContext.saveCellData(newDateData, resultCallback: (result) { + updateCalData(Option calData, Option timeFormatError) { + if (!isClosed) add(DateCalEvent.didUpdateCalData(calData, timeFormatError)); + } + + cellContext.saveCellData(newCalData, resultCallback: (result) { result.fold( - () => emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: none(), - )), + () => updateCalData(Some(newCalData), none()), (err) { switch (ErrorCode.valueOf(err.code)!) { case ErrorCode.InvalidDateTimeFormat: - emit(state.copyWith( - dateData: Some(newDateData), - timeFormatError: Some(timeFormatPrompt(err)), - )); + updateCalData(none(), Some(timeFormatPrompt(err))); break; default: Log.error(err); @@ -168,7 +171,7 @@ class DateCalBloc extends Bloc { ); result.fold( - (l) => emit(state.copyWith(dateTypeOption: newDateTypeOption)), + (l) => emit(state.copyWith(dateTypeOption: newDateTypeOption, timeHintText: _timeHintText(newDateTypeOption))), (err) => Log.error(err), ); } @@ -185,6 +188,8 @@ class DateCalEvent with _$DateCalEvent { const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime; const factory DateCalEvent.setTime(String time) = _Time; const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; + const factory DateCalEvent.didUpdateCalData(Option data, Option timeFormatError) = + _DidUpdateCalData; } @freezed @@ -194,36 +199,48 @@ class DateCalState with _$DateCalState { required CalendarFormat format, required DateTime focusedDay, required Option timeFormatError, - required Option dateData, + required Option calData, required String? time, + required String timeHintText, }) = _DateCalState; factory DateCalState.initial( DateTypeOption dateTypeOption, DateCellData? cellData, ) { - Option dateData = dateDataFromCellData(cellData); - final time = dateData.foldRight("", (dateData, previous) => dateData.time); + Option calData = calDataFromCellData(cellData); + final time = calData.foldRight("", (dateData, previous) => dateData.time); return DateCalState( dateTypeOption: dateTypeOption, format: CalendarFormat.month, focusedDay: DateTime.now(), time: time, - dateData: dateData, + calData: calData, timeFormatError: none(), + timeHintText: _timeHintText(dateTypeOption), ); } } -Option dateDataFromCellData(DateCellData? cellData) { +String _timeHintText(DateTypeOption typeOption) { + switch (typeOption.timeFormat) { + case TimeFormat.TwelveHour: + return LocaleKeys.grid_date_timeHintTextInTwelveHour.tr(); + case TimeFormat.TwentyFourHour: + return LocaleKeys.grid_date_timeHintTextInTwentyFourHour.tr(); + } + return ""; +} + +Option calDataFromCellData(DateCellData? cellData) { String? time = timeFromCellData(cellData); - Option dateData = none(); + Option calData = none(); if (cellData != null) { final timestamp = cellData.timestamp * 1000; final date = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt()); - dateData = Some(DateCalData(date: date, time: time)); + calData = Some(CalendarData(date: date, time: time)); } - return dateData; + return calData; } $fixnum.Int64 timestampFromDateTime(DateTime dateTime) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart index b06a3d60b3..b9f4c74070 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'cell_service/cell_service.dart'; -import 'package:dartz/dartz.dart'; part 'date_cell_bloc.freezed.dart'; class DateCellBloc extends Bloc { @@ -17,11 +16,7 @@ class DateCellBloc extends Bloc { event.when( initial: () => _startListening(), didReceiveCellUpdate: (DateCellData? cellData) { - if (cellData != null) { - emit(state.copyWith(data: Some(cellData))); - } else { - emit(state.copyWith(data: none())); - } + emit(state.copyWith(data: cellData, dateStr: _dateStrFromCellData(cellData))); }, didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)), ); @@ -60,21 +55,26 @@ class DateCellEvent with _$DateCellEvent { @freezed class DateCellState with _$DateCellState { const factory DateCellState({ - required Option data, + required DateCellData? data, + required String dateStr, required Field field, }) = _DateCellState; factory DateCellState.initial(GridDateCellContext context) { final cellData = context.getCellData(); - Option data = none(); - - if (cellData != null) { - data = Some(cellData); - } return DateCellState( field: context.field, - data: data, + data: cellData, + dateStr: _dateStrFromCellData(cellData), ); } } + +String _dateStrFromCellData(DateCellData? cellData) { + String dateStr = ""; + if (cellData != null) { + dateStr = cellData.date + " " + cellData.time; + } + return dateStr; +} 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 c644ac61a5..9855b98bf5 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 @@ -64,7 +64,7 @@ class _DateCellState extends State { cursor: SystemMouseCursors.click, child: Align( alignment: alignment, - child: FlowyText.medium(state.data.foldRight("", (data, _) => data.date), fontSize: 12), + child: FlowyText.medium(state.dateStr, fontSize: 12), ), ), ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart index 016eb00e85..93d304cd1a 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_editor.dart @@ -160,18 +160,21 @@ class _CellCalendarWidget extends StatelessWidget { ), ), selectedDayPredicate: (day) { - return state.dateData.fold( + return state.calData.fold( () => false, (dateData) => isSameDay(dateData.date, day), ); }, onDaySelected: (selectedDay, focusedDay) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.selectDay(selectedDay)); }, onFormatChanged: (format) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.setCalFormat(format)); }, onPageChanged: (focusedDay) { + _CalDateTimeSetting.hide(context); context.read().add(DateCalEvent.setFocusedDay(focusedDay)); }, ); @@ -234,6 +237,7 @@ class _TimeTextFieldState extends State<_TimeTextField> { if (widget.bloc.state.dateTypeOption.includeTime) { _focusNode.addListener(() { if (mounted) { + _CalDateTimeSetting.hide(context); widget.bloc.add(DateCalEvent.setTime(_controller.text)); } }); @@ -257,6 +261,7 @@ class _TimeTextFieldState extends State<_TimeTextField> { child: RoundedInputField( height: 40, focusNode: _focusNode, + hintText: state.timeHintText, controller: _controller, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), normalBorderColor: theme.shader4, @@ -326,6 +331,7 @@ class _CalDateTimeSetting extends StatefulWidget { } void show(BuildContext context) { + hide(context); FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( child: this, @@ -337,6 +343,10 @@ class _CalDateTimeSetting extends StatefulWidget { anchorOffset: const Offset(20, 0), ); } + + static void hide(BuildContext context) { + FlowyOverlay.of(context).remove(identifier()); + } } class _CalDateTimeSettingState extends State<_CalDateTimeSetting> { 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 c8d14b6b03..995a0e5e33 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 @@ -42,7 +42,7 @@ 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, encoded_data: T, 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 c96166272a..2323a6eb20 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 @@ -4,7 +4,7 @@ use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; -use chrono::NaiveDateTime; +use chrono::{NaiveDateTime, Timelike}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ @@ -29,35 +29,36 @@ pub struct DateTypeOption { impl_type_option!(DateTypeOption, FieldType::DateTime); impl DateTypeOption { - fn today_desc_from_timestamp(&self, timestamp: i64, time: &Option) -> String { - let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); - self.today_desc_from_native(native, time) - } - #[allow(dead_code)] - fn today_desc_from_str(&self, s: String, time: &Option) -> String { - match NaiveDateTime::parse_from_str(&s, &self.date_fmt(time)) { - Ok(native) => self.today_desc_from_native(native, time), - Err(_) => "".to_owned(), + pub fn new() -> Self { + Self::default() + } + + fn today_desc_from_timestamp(&self, timestamp: i64) -> DateCellData { + let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); + self.date_from_native(native) + } + + fn date_from_native(&self, native: chrono::NaiveDateTime) -> DateCellData { + if native.timestamp() == 0 { + return DateCellData::default(); } - } - fn today_desc_from_native(&self, native: chrono::NaiveDateTime, time: &Option) -> String { + let time = native.time(); + let has_time = time.hour() != 0 || time.second() != 0; + let utc = self.utc_date_time_from_native(native); - // let china_timezone = FixedOffset::east(8 * 3600); - // let a = utc.with_timezone(&china_timezone); - let fmt = self.date_fmt(time); - let output = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))); - output - } + let fmt = self.date_format.format_str(); + let date = format!("{}", utc.format_with_items(StrftimeItems::new(fmt))); - fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { - let native = NaiveDateTime::from_timestamp(timestamp, 0); - self.utc_date_time_from_native(native) - } + let mut time = "".to_string(); + if has_time { + let fmt = format!("{} {}", self.date_format.format_str(), self.time_format.format_str()); + time = format!("{}", utc.format_with_items(StrftimeItems::new(&fmt))).replace(&date, ""); + } - fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { - chrono::DateTime::::from_utc(naive, chrono::Utc) + let timestamp = native.timestamp(); + DateCellData { date, time, timestamp } } fn date_fmt(&self, time: &Option) -> String { @@ -77,14 +78,6 @@ impl DateTypeOption { } } - fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String { - if serde_cell_data.timestamp == 0 { - return "".to_owned(); - } - - self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time) - } - fn timestamp_from_utc_with_time( &self, utc: &chrono::DateTime, @@ -113,9 +106,18 @@ impl DateTypeOption { Ok(utc.timestamp()) } + + fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime { + let native = NaiveDateTime::from_timestamp(timestamp, 0); + self.utc_date_time_from_native(native) + } + + fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime { + chrono::DateTime::::from_utc(naive, chrono::Utc) + } } -impl CellDataOperation, DateCellDataSerde> for DateTypeOption { +impl CellDataOperation> for DateTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -134,14 +136,11 @@ impl CellDataOperation, DateCellDataSerde> fo } let encoded_data = encoded_data.into().try_into_inner()?; - let date = self.date_desc_from_timestamp(&encoded_data); - let time = encoded_data.time.unwrap_or_else(|| "".to_owned()); - let timestamp = encoded_data.timestamp; - - DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp }) + let date = self.today_desc_from_timestamp(encoded_data.timestamp); + DecodedCellData::try_from_bytes(date) } - fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result + fn apply_changeset(&self, changeset: C, _cell_meta: Option) -> Result where C: Into, { @@ -153,13 +152,13 @@ impl CellDataOperation, DateCellDataSerde> fo let time = Some(time.trim().to_uppercase()); let utc = self.utc_date_time_from_timestamp(date_timestamp); let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; - DateCellDataSerde::new(timestamp, time, &self.time_format) + DateCellDataSerde::new(timestamp, time) } - _ => DateCellDataSerde::from_timestamp(date_timestamp, Some(default_time_str(&self.time_format))), + _ => DateCellDataSerde::new(date_timestamp, None), }, }; - Ok(cell_data) + Ok(cell_data.to_string()) } } @@ -284,21 +283,16 @@ pub struct DateCellData { } #[derive(Default, Serialize, Deserialize)] -pub struct DateCellDataSerde { +pub(crate) struct DateCellDataSerde { pub timestamp: i64, + + // #[deprecated(since = "0.0.4", note = "No need to same the time that user input")] pub time: Option, } impl DateCellDataSerde { - fn new(timestamp: i64, time: Option, time_format: &TimeFormat) -> Self { - Self { - timestamp, - time: Some(time.unwrap_or_else(|| default_time_str(time_format))), - } - } - - pub(crate) fn from_timestamp(timestamp: i64, time: Option) -> Self { - Self { timestamp, time } + pub(crate) fn new(timestamp: i64, _time: Option) -> Self { + Self { timestamp, time: None } } } @@ -316,13 +310,6 @@ impl ToString for DateCellDataSerde { } } -fn default_time_str(time_format: &TimeFormat) -> String { - match time_format { - TimeFormat::TwelveHour => "12:00 AM".to_string(), - TimeFormat::TwentyFourHour => "00:00".to_string(), - } -} - #[derive(Clone, Debug, Default, ProtoBuf)] pub struct DateChangesetPayload { #[pb(index = 1)] @@ -403,11 +390,11 @@ mod tests { DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat, }; use crate::services::row::{CellDataOperation, EncodedCellData}; - use flowy_grid_data_model::entities::{FieldMeta, FieldType}; + use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; #[test] - fn date_description_invalid_input_test() { + fn date_type_option_invalid_input_test() { let type_option = DateTypeOption::default(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); @@ -424,7 +411,7 @@ mod tests { } #[test] - fn date_description_date_format_test() { + fn date_type_option_date_format_test() { let mut type_option = DateTypeOption::default(); let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); for date_format in DateFormat::iter() { @@ -447,7 +434,7 @@ mod tests { } #[test] - fn date_description_time_format_test() { + fn date_type_option_time_format_test() { let mut type_option = DateTypeOption::default(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); @@ -465,7 +452,7 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 00:00", + "May 27,2022", ); assert_changeset_result( &type_option, @@ -487,9 +474,9 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 12:00 AM", + "May 27,2022", ); - + // assert_changeset_result( &type_option, DateCellContentChangeset { @@ -517,8 +504,8 @@ mod tests { } #[test] - fn date_description_apply_changeset_test() { - let mut type_option = DateTypeOption::default(); + fn date_type_option_apply_changeset_test() { + let mut type_option = DateTypeOption::new(); let field_type = FieldType::DateTime; let field_meta = FieldBuilder::from_field_type(&field_type).build(); let date_timestamp = "1653609600".to_owned(); @@ -543,7 +530,7 @@ mod tests { }, &field_type, &field_meta, - "May 27,2022 00:00", + "May 27,2022", ); assert_changeset_result( @@ -572,30 +559,53 @@ mod tests { #[test] #[should_panic] - fn date_description_apply_changeset_error_test() { - let mut type_option = DateTypeOption::default(); + fn date_type_option_apply_changeset_error_test() { + let mut type_option = DateTypeOption::new(); type_option.include_time = true; - let _field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build(); + let field_meta = FieldBuilder::from_field_type(&type_option.field_type()).build(); let date_timestamp = "1653609600".to_owned(); - let changeset = DateCellContentChangeset { - date: Some(date_timestamp.clone()), - time: Some("1:a0".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); - let changeset = DateCellContentChangeset { - date: Some(date_timestamp), - time: Some("1:".to_owned()), - }; - let _ = type_option.apply_changeset(changeset, None).unwrap(); + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); } #[test] #[should_panic] - fn date_description_invalid_data_test() { - let type_option = DateTypeOption::default(); - type_option.apply_changeset("he", None).unwrap(); + fn date_type_option_twelve_hours_to_twenty_four_hours() { + let mut type_option = DateTypeOption::new(); + type_option.include_time = true; + let field_meta = FieldBuilder::from_field_type(&type_option.field_type()).build(); + let date_timestamp = "1653609600".to_owned(); + + assert_changeset_result( + &type_option, + DateCellContentChangeset { + date: Some(date_timestamp.clone()), + time: Some("1:00 am".to_owned()), + }, + &type_option.field_type(), + &field_meta, + "May 27,2022 01:00", + ); } fn assert_changeset_result( @@ -605,7 +615,7 @@ mod tests { field_meta: &FieldMeta, expected: &str, ) { - let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap())); + let encoded_data = type_option.apply_changeset(changeset, None).unwrap(); assert_eq!( expected.to_owned(), decode_cell_data(encoded_data, type_option, field_meta) @@ -613,11 +623,19 @@ mod tests { } fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) { - let serde_json = DateCellDataSerde { timestamp, time: None }.to_string(); + let encoded_data = type_option + .apply_changeset( + DateCellContentChangeset { + date: Some(timestamp.to_string()), + time: None, + }, + None, + ) + .unwrap(); assert_eq!( expected.to_owned(), - decode_cell_data(serde_json, type_option, field_meta) + decode_cell_data(encoded_data, type_option, field_meta) ); } @@ -626,11 +644,16 @@ mod tests { type_option: &DateTypeOption, field_meta: &FieldMeta, ) -> String { - type_option + let decoded_data = type_option .decode_cell_data(encoded_data, &FieldType::DateTime, field_meta) .unwrap() .parse::() - .unwrap() - .date + .unwrap(); + + if type_option.include_time { + format!("{}{}", decoded_data.date, decoded_data.time) + } else { + format!("{}", decoded_data.date) + } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs index f1000cbcfa..bb3130c597 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -71,7 +71,7 @@ pub struct NumberTypeOption { } impl_type_option!(NumberTypeOption, FieldType::Number); -impl CellDataOperation for NumberTypeOption { +impl CellDataOperation for NumberTypeOption { fn decode_cell_data( &self, encoded_data: T, 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 81a7ff5c04..a7ad30cc31 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,7 +95,7 @@ impl SelectOptionOperation for SingleSelectTypeOption { } } -impl CellDataOperation for SingleSelectTypeOption { +impl CellDataOperation for SingleSelectTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -193,7 +193,7 @@ impl SelectOptionOperation for MultiSelectTypeOption { } } -impl CellDataOperation for MultiSelectTypeOption { +impl CellDataOperation for MultiSelectTypeOption { fn decode_cell_data( &self, encoded_data: T, 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 3509972670..029d476533 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 @@ -31,7 +31,7 @@ pub struct RichTextTypeOption { } impl_type_option!(RichTextTypeOption, FieldType::RichText); -impl CellDataOperation for RichTextTypeOption { +impl CellDataOperation for RichTextTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -80,7 +80,7 @@ mod tests { // date let field_type = FieldType::DateTime; let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); - let json = serde_json::to_string(&DateCellDataSerde::from_timestamp(1647251762, None)).unwrap(); + let json = serde_json::to_string(&DateCellDataSerde::new(1647251762, None)).unwrap(); assert_eq!( type_option .decode_cell_data(json, &field_type, &date_time_field_meta) diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs index 0d5f37baaf..ecb2a8e16f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs @@ -34,7 +34,7 @@ pub struct URLTypeOption { } impl_type_option!(URLTypeOption, FieldType::URL); -impl CellDataOperation, String> for URLTypeOption { +impl CellDataOperation> for URLTypeOption { fn decode_cell_data( &self, encoded_data: T, 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 d93f84244a..7423bf0fcd 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 @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Formatter; use std::str::FromStr; -pub trait CellDataOperation { +pub trait CellDataOperation { fn decode_cell_data( &self, encoded_data: T, @@ -14,14 +14,14 @@ pub trait CellDataOperation { field_meta: &FieldMeta, ) -> FlowyResult where - T: Into; + T: Into; // fn apply_changeset>( &self, changeset: C, cell_meta: Option, - ) -> FlowyResult; + ) -> FlowyResult; } #[derive(Debug)] @@ -128,9 +128,7 @@ 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) - .map(|data| data.to_string()), + FieldType::DateTime => DateTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::SingleSelect => SingleSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::MultiSelect => MultiSelectTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), FieldType::Checkbox => CheckboxTypeOption::from(field_meta).apply_changeset(changeset, cell_meta), From cad5e5d718430583bd63502275fa5f2cd98928d3 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 18:15:10 +0800 Subject: [PATCH 59/82] refactor: Date's cell data serde --- .../cell/cell_service/context_builder.dart | 1 + .../field/type_options/date_type_option.rs | 53 ++++--------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 4a27b10e1b..b554f03556 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -31,6 +31,7 @@ class GridCellContextBuilder { final cellDataLoader = GridCellDataLoader( gridCell: _gridCell, parser: DateCellDataParser(), + config: const GridCellDataConfig(reloadOnFieldChanged: true), ); return GridDateCellContext( 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 2323a6eb20..891eb2f336 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,17 +1,16 @@ use crate::entities::{CellIdentifier, CellIdentifierPayload}; use crate::impl_type_option; use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder}; -use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData}; +use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData}; use bytes::Bytes; use chrono::format::strftime::StrftimeItems; use chrono::{NaiveDateTime, Timelike}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; +use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::entities::{ CellChangeset, CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry, }; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use strum_macros::EnumIter; // Date @@ -117,7 +116,7 @@ impl DateTypeOption { } } -impl CellDataOperation> for DateTypeOption { +impl CellDataOperation for DateTypeOption { fn decode_cell_data( &self, encoded_data: T, @@ -125,7 +124,7 @@ impl CellDataOperation> for DateTypeOption { _field_meta: &FieldMeta, ) -> FlowyResult where - T: Into>, + T: Into, { // Return default data if the type_option_cell_data is not FieldType::DateTime. // It happens when switching from one field to another. @@ -135,8 +134,8 @@ impl CellDataOperation> for DateTypeOption { return Ok(DecodedCellData::default()); } - let encoded_data = encoded_data.into().try_into_inner()?; - let date = self.today_desc_from_timestamp(encoded_data.timestamp); + let timestamp = encoded_data.into().parse::().unwrap_or(0); + let date = self.today_desc_from_timestamp(timestamp); DecodedCellData::try_from_bytes(date) } @@ -146,15 +145,15 @@ impl CellDataOperation> for DateTypeOption { { let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?; let cell_data = match content_changeset.date_timestamp() { - None => DateCellDataSerde::default(), + None => 0, Some(date_timestamp) => match (self.include_time, content_changeset.time) { (true, Some(time)) => { let time = Some(time.trim().to_uppercase()); let utc = self.utc_date_time_from_timestamp(date_timestamp); let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; - DateCellDataSerde::new(timestamp, time) + timestamp } - _ => DateCellDataSerde::new(date_timestamp, None), + _ => date_timestamp, }, }; @@ -282,34 +281,6 @@ pub struct DateCellData { pub timestamp: i64, } -#[derive(Default, Serialize, Deserialize)] -pub(crate) struct DateCellDataSerde { - pub timestamp: i64, - - // #[deprecated(since = "0.0.4", note = "No need to same the time that user input")] - pub time: Option, -} - -impl DateCellDataSerde { - pub(crate) fn new(timestamp: i64, _time: Option) -> Self { - Self { timestamp, time: None } - } -} - -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_else(|_| "".to_string()) - } -} - #[derive(Clone, Debug, Default, ProtoBuf)] pub struct DateChangesetPayload { #[pb(index = 1)] @@ -386,9 +357,7 @@ 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, DateCellData, DateFormat, DateTypeOption, TimeFormat}; use crate::services::row::{CellDataOperation, EncodedCellData}; use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; @@ -639,7 +608,7 @@ mod tests { ); } - fn decode_cell_data>>( + fn decode_cell_data>( encoded_data: T, type_option: &DateTypeOption, field_meta: &FieldMeta, From 4f250a9130a91b3caf6671854228bb1e2a626553 Mon Sep 17 00:00:00 2001 From: Moksh Mahajan Date: Thu, 2 Jun 2022 01:34:45 +0530 Subject: [PATCH 60/82] fix: clicking on option capsule will now create and select the new option value --- .../widgets/cell/select_option_cell/select_option_editor.dart | 1 + 1 file changed, 1 insertion(+) 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 1829157f51..798444213c 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 @@ -207,6 +207,7 @@ class _CreateOptionCell extends StatelessWidget { SelectOptionTag( name: name, color: theme.shader6, + onSelected: () => context.read().add(SelectOptionEditorEvent.newOption(name)), ), ], ); From 481fd9df514334ac55fa6ec553651dc3cee7d2c4 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 20:24:25 +0800 Subject: [PATCH 61/82] fix: rust unit test --- .../application/grid/cell/cell_service/context_builder.dart | 1 + .../src/services/field/type_options/date_type_option.rs | 2 +- .../src/services/field/type_options/text_type_option.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index b554f03556..3041c563d9 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -219,6 +219,7 @@ class _GridCellContext extends Equatable { } void dispose() { + _cellListener.stop(); _loadDataOperation?.cancel(); _saveDataOperation?.cancel(); 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 891eb2f336..f91fc8bb48 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 @@ -358,7 +358,7 @@ impl std::convert::From for CellContentChangeset { mod tests { use crate::services::field::FieldBuilder; use crate::services::field::{DateCellContentChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; - use crate::services::row::{CellDataOperation, EncodedCellData}; + use crate::services::row::CellDataOperation; use flowy_grid_data_model::entities::{FieldMeta, FieldType, TypeOptionDataEntry}; use strum::IntoEnumIterator; 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 029d476533..b59681a2ea 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 @@ -80,10 +80,10 @@ mod tests { // date let field_type = FieldType::DateTime; let date_time_field_meta = FieldBuilder::from_field_type(&field_type).build(); - let json = serde_json::to_string(&DateCellDataSerde::new(1647251762, None)).unwrap(); + assert_eq!( type_option - .decode_cell_data(json, &field_type, &date_time_field_meta) + .decode_cell_data(1647251762.to_string(), &field_type, &date_time_field_meta) .unwrap() .parse::() .unwrap() From 72e26ab229415f2f4f6f43db316999fab604113d Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 2 Jun 2022 21:57:12 +0800 Subject: [PATCH 62/82] fix: disable set decimal scale --- .../field/type_options/date_type_option.rs | 9 +- .../number_type_option/number_type_option.rs | 180 +++++++++--------- 2 files changed, 92 insertions(+), 97 deletions(-) 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 f91fc8bb48..4a64fc3eca 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 @@ -150,8 +150,7 @@ impl CellDataOperation for DateTypeOption { (true, Some(time)) => { let time = Some(time.trim().to_uppercase()); let utc = self.utc_date_time_from_timestamp(date_timestamp); - let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?; - timestamp + self.timestamp_from_utc_with_time(&utc, &time)? } _ => date_timestamp, }, @@ -548,7 +547,7 @@ mod tests { assert_changeset_result( &type_option, DateCellContentChangeset { - date: Some(date_timestamp.clone()), + date: Some(date_timestamp), time: Some("1:00".to_owned()), }, &type_option.field_type(), @@ -568,7 +567,7 @@ mod tests { assert_changeset_result( &type_option, DateCellContentChangeset { - date: Some(date_timestamp.clone()), + date: Some(date_timestamp), time: Some("1:00 am".to_owned()), }, &type_option.field_type(), @@ -622,7 +621,7 @@ mod tests { if type_option.include_time { format!("{}{}", decoded_data.date, decoded_data.time) } else { - format!("{}", decoded_data.date) + decoded_data.date } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs index bb3130c597..82970ab25a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -71,6 +71,76 @@ pub struct NumberTypeOption { } impl_type_option!(NumberTypeOption, FieldType::Number); +impl NumberTypeOption { + pub fn new() -> Self { + Self::default() + } + + fn cell_content_from_number_str(&self, s: &str) -> FlowyResult { + return match self.format { + NumberFormat::Number => { + if let Ok(v) = s.parse::() { + return Ok(v.to_string()); + } + + if let Ok(v) = s.parse::() { + return Ok(v.to_string()); + } + + Ok("".to_string()) + } + NumberFormat::Percent => { + let content = s.parse::().map_or(String::new(), |v| v.to_string()); + Ok(content) + } + _ => self.money_from_number_str(s), + }; + } + + pub fn set_format(&mut self, format: NumberFormat) { + self.format = format; + self.symbol = format.symbol(); + } + + fn money_from_number_str(&self, s: &str) -> FlowyResult { + let mut number = self.strip_currency_symbol(s); + + if s.is_empty() { + return Ok("".to_string()); + } + + match Decimal::from_str(&number) { + Ok(mut decimal) => { + decimal.set_sign_positive(self.sign_positive); + let money = rusty_money::Money::from_decimal(decimal, self.format.currency()).to_string(); + Ok(money) + } + Err(_) => match rusty_money::Money::from_str(&number, self.format.currency()) { + Ok(money) => Ok(money.to_string()), + Err(_) => { + number.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); + if number.chars().all(char::is_numeric) { + self.money_from_number_str(&number) + } else { + Err(FlowyError::invalid_data().context("Should only contain numbers")) + } + } + }, + } + } + + fn strip_currency_symbol(&self, s: T) -> String { + let mut s = s.to_string(); + for symbol in CURRENCY_SYMBOL.iter() { + if s.starts_with(symbol) { + s = s.strip_prefix(symbol).unwrap_or("").to_string(); + break; + } + } + s + } +} + impl CellDataOperation for NumberTypeOption { fn decode_cell_data( &self, @@ -103,7 +173,7 @@ impl CellDataOperation for NumberTypeOption { Ok(DecodedCellData::new(content)) } _ => { - let content = self.number_from_str(&cell_data); + let content = self.money_from_number_str(&cell_data).unwrap_or("".to_string()); Ok(DecodedCellData::new(content)) } } @@ -114,15 +184,8 @@ impl CellDataOperation for NumberTypeOption { C: Into, { let changeset = changeset.into(); - let mut data = changeset.trim().to_string(); - - if self.format != NumberFormat::Number { - data = self.strip_symbol(data); - if !data.chars().all(char::is_numeric) { - return Err(FlowyError::invalid_data().context("Should only contain numbers")); - } - } - + let data = changeset.trim().to_string(); + let _ = self.cell_content_from_number_str(&data)?; Ok(data) } } @@ -141,54 +204,6 @@ impl std::default::Default for NumberTypeOption { } } -impl NumberTypeOption { - pub fn set_format(&mut self, format: NumberFormat) { - self.format = format; - self.symbol = format.symbol(); - } - - fn number_from_str(&self, s: &str) -> String { - match Decimal::from_str(s) { - Ok(mut decimal) => { - match decimal.set_scale(self.scale) { - Ok(_) => {} - Err(e) => { - tracing::error!("Set decimal scale failed: {:?}", e); - } - } - - decimal.set_sign_positive(self.sign_positive); - let money = rusty_money::Money::from_decimal(decimal, self.format.currency()); - money.to_string() - } - Err(_) => { - let s = self.strip_symbol(s); - if !s.is_empty() && s.chars().all(char::is_numeric) { - self.number_from_str(&s) - } else { - "".to_owned() - } - } - } - } - - fn strip_symbol(&self, s: T) -> String { - let mut s = s.to_string(); - - for symbol in CURRENCY_SYMBOL.iter() { - if s.starts_with(symbol) { - s = s.strip_prefix(symbol).unwrap_or("").to_string(); - break; - } - } - - if !s.chars().all(char::is_numeric) { - s.retain(|c| !STRIP_SYMBOL.contains(&c.to_string())); - } - s - } -} - #[cfg(test)] mod tests { use crate::services::field::FieldBuilder; @@ -206,14 +221,21 @@ mod tests { assert_equal(&type_option, "abc", "", &field_type, &field_meta); } + #[test] + fn number_type_option_strip_symbol_test() { + let mut type_option = NumberTypeOption::new(); + type_option.format = NumberFormat::USD; + assert_eq!(type_option.strip_currency_symbol("$18,443"), "18,443".to_owned()); + + type_option.format = NumberFormat::Yuan; + assert_eq!(type_option.strip_currency_symbol("$0.2"), "0.2".to_owned()); + } + #[test] fn number_type_option_format_number_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; @@ -248,10 +270,12 @@ mod tests { type_option.format = format; match format { NumberFormat::Number => { - // assert_equal(&type_option, "18443", "18443", &field_type, &field_meta); + assert_equal(&type_option, "18443", "18443", &field_type, &field_meta); + assert_equal(&type_option, "0.2", "0.2", &field_type, &field_meta); } NumberFormat::USD => { assert_equal(&type_option, "$18,44", "$1,844", &field_type, &field_meta); + assert_equal(&type_option, "$0.2", "$0.2", &field_type, &field_meta); assert_equal(&type_option, "", "", &field_type, &field_meta); assert_equal(&type_option, "abc", "", &field_type, &field_meta); } @@ -264,7 +288,8 @@ mod tests { assert_equal(&type_option, "CN¥1844", "CN¥1,844", &field_type, &field_meta); } NumberFormat::EUR => { - assert_equal(&type_option, "€18.44", "€1.844", &field_type, &field_meta); + assert_equal(&type_option, "€18.44", "€18,44", &field_type, &field_meta); + assert_equal(&type_option, "€0.5", "€0,5", &field_type, &field_meta); assert_equal(&type_option, "€1844", "€1.844", &field_type, &field_meta); } _ => {} @@ -272,35 +297,6 @@ mod tests { } } - #[test] - fn number_type_option_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 { From cf6ad8575f212490167093ddf8634a1d22f2251f Mon Sep 17 00:00:00 2001 From: Moksh Mahajan Date: Wed, 1 Jun 2022 23:49:16 +0530 Subject: [PATCH 63/82] fix: close renaming window when creating another new option --- .../widgets/cell/select_option_cell/select_option_editor.dart | 1 + .../grid/src/widgets/cell/select_option_cell/text_field.dart | 3 +++ .../src/widgets/header/type_option/select_option_editor.dart | 2 ++ 3 files changed, 6 insertions(+) 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 1829157f51..e9dc6afa96 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 @@ -156,6 +156,7 @@ class _TextField extends StatelessWidget { selectedOptionMap: optionMap, distanceToText: _editorPannelWidth * 0.7, tagController: _tagController, + onClick: () => FlowyOverlay.of(context).remove(SelectOptionTypeOptionEditor.identifier), newText: (text) { context.read().add(SelectOptionEditorEvent.filterOption(text)); }, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart index 125a86a609..398d98a994 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/text_field.dart @@ -22,6 +22,7 @@ class SelectOptionTextField extends StatelessWidget { final Function(String) onNewTag; final Function(String) newText; + final VoidCallback? onClick; SelectOptionTextField({ required this.options, @@ -30,6 +31,7 @@ class SelectOptionTextField extends StatelessWidget { required this.tagController, required this.onNewTag, required this.newText, + this.onClick, TextEditingController? controller, FocusNode? focusNode, Key? key, @@ -53,6 +55,7 @@ class SelectOptionTextField extends StatelessWidget { autofocus: true, controller: editController, focusNode: focusNode, + onTap: onClick, onChanged: (text) { if (onChanged != null) { onChanged(text); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart index dada59b41c..cec32bd99e 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/select_option_editor.dart @@ -25,6 +25,8 @@ class SelectOptionTypeOptionEditor extends StatelessWidget { Key? key, }) : super(key: key); + static String get identifier => (SelectOptionTypeOptionEditor).toString(); + @override Widget build(BuildContext context) { return BlocProvider( From 2342a9b8343b003472fe3e32654f2c87d5b14292 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 3 Jun 2022 10:51:24 +0800 Subject: [PATCH 64/82] fix: expand the list width after field's width changed --- .../workspace/application/grid/grid_bloc.dart | 38 ++++++++++++++----- .../plugins/grid/src/grid_page.dart | 4 +- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart index 357ccadcd9..16b4beb041 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:dartz/dartz.dart'; +import 'package:equatable/equatable.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart'; @@ -8,6 +9,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'cell/cell_service/cell_service.dart'; import 'grid_service.dart'; import 'row/row_service.dart'; +import 'dart:collection'; part 'grid_bloc.freezed.dart'; @@ -33,19 +35,19 @@ class GridBloc extends Bloc { on( (event, emit) async { - await event.map( - initial: (InitialGrid value) async { + await event.when( + initial: () async { _startListening(); await _loadGrid(emit); }, - createRow: (_CreateRow value) { + createRow: () { _gridService.createRow(); }, - didReceiveRowUpdate: (_DidReceiveRowUpdate value) { - emit(state.copyWith(rows: value.rows, listState: value.listState)); + didReceiveRowUpdate: (rows, listState) { + emit(state.copyWith(rows: rows, listState: listState)); }, - didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { - emit(state.copyWith(rows: rowCache.clonedRows, fields: value.fields)); + didReceiveFieldUpdate: (fields) { + emit(state.copyWith(rows: rowCache.clonedRows, fields: GridFieldEquatable(fields))); }, ); }, @@ -93,7 +95,7 @@ class GridBloc extends Bloc { emit(state.copyWith( grid: Some(grid), - fields: fieldCache.fields, + fields: GridFieldEquatable(fieldCache.fields), rows: rowCache.clonedRows, loadingState: GridLoadingState.finish(left(unit)), )); @@ -117,14 +119,14 @@ class GridState with _$GridState { const factory GridState({ required String gridId, required Option grid, - required List fields, + required GridFieldEquatable fields, required List rows, required GridLoadingState loadingState, required GridRowChangeReason listState, }) = _GridState; factory GridState.initial(String gridId) => GridState( - fields: [], + fields: const GridFieldEquatable([]), rows: [], grid: none(), gridId: gridId, @@ -138,3 +140,19 @@ class GridLoadingState with _$GridLoadingState { const factory GridLoadingState.loading() = _Loading; const factory GridLoadingState.finish(Either successOrFail) = _Finish; } + +class GridFieldEquatable extends Equatable { + final List _fields; + + const GridFieldEquatable(List fields) : _fields = fields; + + @override + List get props { + return [ + _fields.length, + _fields.map((field) => field.width).reduce((value, element) => value + element), + ]; + } + + UnmodifiableListView get value => UnmodifiableListView(_fields); +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart index 5a913137af..b8591895e8 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart @@ -91,9 +91,9 @@ class _FlowyGridState extends State { @override Widget build(BuildContext context) { return BlocBuilder( - buildWhen: (previous, current) => previous.fields.length != current.fields.length, + buildWhen: (previous, current) => previous.fields != current.fields, builder: (context, state) { - final contentWidth = GridLayout.headerWidth(state.fields); + final contentWidth = GridLayout.headerWidth(state.fields.value); final child = _wrapScrollView( contentWidth, [ From 4887885aaef17c2e4bd5704b0b61e0e4dd63ad97 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 3 Jun 2022 12:05:47 +0800 Subject: [PATCH 65/82] refactor: add GridCellState and GridFocusNodeCellState --- .../grid/src/widgets/cell/cell_builder.dart | 70 ++++++++++++++++++- .../grid/src/widgets/cell/checkbox_cell.dart | 22 ++---- .../src/widgets/cell/date_cell/date_cell.dart | 2 +- .../grid/src/widgets/cell/number_cell.dart | 38 ++-------- .../select_option_cell.dart | 4 +- .../grid/src/widgets/cell/text_cell.dart | 42 ++--------- .../src/widgets/cell/url_cell/url_cell.dart | 21 ++---- 7 files changed, 95 insertions(+), 104 deletions(-) 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 65bff03ecf..05708e6198 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 @@ -48,7 +48,9 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget implements AccessoryWidget, CellContainerFocustable { +abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget, CellContainerFocustable { + GridCellWidget({Key? key}) : super(key: key); + @override final ValueNotifier isFocus = ValueNotifier(false); @@ -59,6 +61,72 @@ abstract class GridCellWidget implements AccessoryWidget, CellContainerFocustabl final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus(); } +abstract class GridCellState extends State { + @override + void initState() { + widget.requestBeginFocus.setListener(() => requestBeginFocus()); + super.initState(); + } + + @override + void didUpdateWidget(covariant T oldWidget) { + if (oldWidget != this) { + widget.requestBeginFocus.setListener(() => requestBeginFocus()); + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + widget.requestBeginFocus.removeAllListener(); + super.dispose(); + } + + void requestBeginFocus(); +} + +abstract class GridFocusNodeCellState extends GridCellState { + SingleListenrFocusNode focusNode = SingleListenrFocusNode(); + + @override + void initState() { + _listenOnFocusNodeChanged(); + super.initState(); + } + + @override + void didUpdateWidget(covariant T oldWidget) { + if (oldWidget != this) { + _listenOnFocusNodeChanged(); + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + focusNode.removeAllListener(); + focusNode.dispose(); + super.dispose(); + } + + @override + void requestBeginFocus() { + if (focusNode.hasFocus == false && focusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(focusNode); + } + } + + void _listenOnFocusNodeChanged() { + widget.isFocus.value = focusNode.hasFocus; + focusNode.setListener(() { + widget.isFocus.value = focusNode.hasFocus; + focusChanged(); + }); + } + + Future focusChanged() async {} +} + class GridCellRequestBeginFocus extends ChangeNotifier { VoidCallback? _listener; 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 78910348b3..6f22a7d977 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 StatefulWidget with GridCellWidget { +class CheckboxCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; CheckboxCell({ required this.cellContextBuilder, @@ -14,17 +14,17 @@ class CheckboxCell extends StatefulWidget with GridCellWidget { }) : super(key: key); @override - State createState() => _CheckboxCellState(); + GridCellState createState() => _CheckboxCellState(); } -class _CheckboxCellState extends State { +class _CheckboxCellState extends GridCellState { late CheckboxCellBloc _cellBloc; @override void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const CheckboxCellEvent.initial()); - _handleRequestFocus(); + super.initState(); } @@ -49,22 +49,14 @@ class _CheckboxCellState extends State { ); } - @override - void didUpdateWidget(covariant CheckboxCell oldWidget) { - _handleRequestFocus(); - super.didUpdateWidget(oldWidget); - } - @override Future dispose() async { - widget.requestBeginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } - void _handleRequestFocus() { - widget.requestBeginFocus.setListener(() { - _cellBloc.add(const CheckboxCellEvent.select()); - }); + @override + void requestBeginFocus() { + _cellBloc.add(const CheckboxCellEvent.select()); } } 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 9855b98bf5..eed45464bf 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 StatefulWidget with GridCellWidget { +class DateCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final DateCellStyle? cellStyle; 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 64fff54c47..88aaf5cb3b 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 StatefulWidget with GridCellWidget { +class NumberCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; NumberCell({ @@ -16,13 +16,12 @@ class NumberCell extends StatefulWidget with GridCellWidget { }) : super(key: key); @override - State createState() => _NumberCellState(); + GridFocusNodeCellState createState() => _NumberCellState(); } -class _NumberCellState extends State { +class _NumberCellState extends GridFocusNodeCellState { late NumberCellBloc _cellBloc; late TextEditingController _controller; - late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -30,14 +29,11 @@ class _NumberCellState extends State { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: contentFromState(_cellBloc.state)); - _focusNode = SingleListenrFocusNode(); - _listenOnFocusNodeChanged(); super.initState(); } @override Widget build(BuildContext context) { - _handleCellRequestFocus(context); return BlocProvider.value( value: _cellBloc, child: MultiBlocListener( @@ -49,8 +45,8 @@ class _NumberCellState extends State { ], child: TextField( controller: _controller, - focusNode: _focusNode, - onEditingComplete: () => _focusNode.unfocus(), + focusNode: focusNode, + onEditingComplete: () => focusNode.unfocus(), maxLines: null, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), decoration: const InputDecoration( @@ -65,20 +61,12 @@ class _NumberCellState extends State { @override Future dispose() async { - widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeAllListener(); - _focusNode.dispose(); super.dispose(); } @override - void didUpdateWidget(covariant NumberCell oldWidget) { - _listenOnFocusNodeChanged(); - super.didUpdateWidget(oldWidget); - } - Future focusChanged() async { if (mounted) { _delayOperation?.cancel(); @@ -90,22 +78,6 @@ class _NumberCellState extends State { } } - void _listenOnFocusNodeChanged() { - widget.isFocus.value = _focusNode.hasFocus; - _focusNode.setListener(() { - widget.isFocus.value = _focusNode.hasFocus; - focusChanged(); - }); - } - - void _handleCellRequestFocus(BuildContext context) { - widget.requestBeginFocus.setListener(() { - if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { - FocusScope.of(context).requestFocus(_focusNode); - } - }); - } - String contentFromState(NumberCellState state) { return state.content.fold((l) => l, (r) => ""); } 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 aa780263b5..294a87976f 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 StatefulWidget with GridCellWidget { +class SingleSelectCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final SelectOptionCellStyle? cellStyle; @@ -74,7 +74,7 @@ class _SingleSelectCellState extends State { } //---------------------------------------------------------------- -class MultiSelectCell extends StatefulWidget with GridCellWidget { +class MultiSelectCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final SelectOptionCellStyle? cellStyle; 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 b2eda288a1..7e1333fff0 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 StatefulWidget with GridCellWidget { +class GridTextCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final GridTextCellStyle? cellStyle; GridTextCell({ @@ -29,13 +29,12 @@ class GridTextCell extends StatefulWidget with GridCellWidget { } @override - State createState() => _GridTextCellState(); + GridFocusNodeCellState createState() => _GridTextCellState(); } -class _GridTextCellState extends State { +class _GridTextCellState extends GridFocusNodeCellState { late TextCellBloc _cellBloc; late TextEditingController _controller; - late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -44,10 +43,6 @@ class _GridTextCellState extends State { _cellBloc = getIt(param1: cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - _focusNode = SingleListenrFocusNode(); - - _listenOnFocusNodeChanged(); - _listenRequestFocus(context); super.initState(); } @@ -63,9 +58,9 @@ class _GridTextCellState extends State { }, child: TextField( controller: _controller, - focusNode: _focusNode, + focusNode: focusNode, onChanged: (value) => focusChanged(), - onEditingComplete: () => _focusNode.unfocus(), + onEditingComplete: () => focusNode.unfocus(), maxLines: null, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), decoration: InputDecoration( @@ -81,39 +76,12 @@ class _GridTextCellState extends State { @override Future dispose() async { - widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeAllListener(); - _focusNode.dispose(); - super.dispose(); } @override - void didUpdateWidget(covariant GridTextCell oldWidget) { - if (oldWidget != widget) { - _listenOnFocusNodeChanged(); - } - super.didUpdateWidget(oldWidget); - } - - void _listenOnFocusNodeChanged() { - widget.isFocus.value = _focusNode.hasFocus; - _focusNode.setListener(() { - widget.isFocus.value = _focusNode.hasFocus; - focusChanged(); - }); - } - - void _listenRequestFocus(BuildContext context) { - widget.requestBeginFocus.setListener(() { - if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { - FocusScope.of(context).requestFocus(_focusNode); - } - }); - } - Future focusChanged() async { if (mounted) { _delayOperation?.cancel(); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 56f0b421a5..5caa014df0 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -30,7 +30,7 @@ enum GridURLCellAccessoryType { copyURL, } -class GridURLCell extends StatefulWidget with GridCellWidget { +class GridURLCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; late final GridURLCellStyle? cellStyle; GridURLCell({ @@ -46,7 +46,7 @@ class GridURLCell extends StatefulWidget with GridCellWidget { } @override - State createState() => _GridURLCellState(); + GridCellState createState() => _GridURLCellState(); GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) { switch (ty) { @@ -78,7 +78,7 @@ class GridURLCell extends StatefulWidget with GridCellWidget { }; } -class _GridURLCellState extends State { +class _GridURLCellState extends GridCellState { late URLCellBloc _cellBloc; @override @@ -86,7 +86,6 @@ class _GridURLCellState extends State { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; _cellBloc = URLCellBloc(cellContext: cellContext); _cellBloc.add(const URLCellEvent.initial()); - _handleRequestFocus(); super.initState(); } @@ -125,17 +124,10 @@ class _GridURLCellState extends State { @override Future dispose() async { - widget.requestBeginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } - @override - void didUpdateWidget(covariant GridURLCell oldWidget) { - _handleRequestFocus(); - super.didUpdateWidget(oldWidget); - } - Future _openUrlOrEdit(String url) async { final uri = Uri.parse(url); if (url.isNotEmpty && await canLaunchUrl(uri)) { @@ -146,10 +138,9 @@ class _GridURLCellState extends State { } } - void _handleRequestFocus() { - widget.requestBeginFocus.setListener(() { - _openUrlOrEdit(_cellBloc.state.url); - }); + @override + void requestBeginFocus() { + _openUrlOrEdit(_cellBloc.state.url); } } From f4a62209df514da574b9a1516097aeadd2077a09 Mon Sep 17 00:00:00 2001 From: Moksh Mahajan Date: Sat, 4 Jun 2022 02:09:55 +0530 Subject: [PATCH 66/82] fix: close emoji picker window on page switch --- .../presentation/widgets/emoji_picker/src/emoji_button.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart index eeafd4ae98..8866b56d5b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart @@ -52,6 +52,12 @@ class _EmojiStyleButtonState extends State { ); } + @override + void dispose() { + _entry.remove(); + super.dispose(); + } + // @override // void didUpdateWidget(covariant FlowyEmojiStyleButton oldWidget) { // super.didUpdateWidget(oldWidget); From 1a83cb65eda8d678178893e7f0b8e1833d53b223 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 4 Jun 2022 11:04:43 +0800 Subject: [PATCH 67/82] chore: click enter to exist edit state --- .../plugins/grid/src/grid_page.dart | 3 +- .../grid/src/widgets/cell/cell_accessory.dart | 6 +- .../grid/src/widgets/cell/cell_builder.dart | 27 +++++---- .../grid/src/widgets/cell/cell_shortcuts.dart | 47 +++++++++++++++ .../grid/src/widgets/cell/text_cell.dart | 1 + .../grid/src/widgets/row/row_detail.dart | 2 +- .../plugins/grid/src/widgets/shortcuts.dart | 58 +++++++++++++++++++ 7 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/shortcuts.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart index b8591895e8..19c94817d8 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart @@ -15,6 +15,7 @@ import 'layout/sizes.dart'; import 'widgets/row/grid_row.dart'; import 'widgets/footer/grid_footer.dart'; import 'widgets/header/grid_header.dart'; +import 'widgets/shortcuts.dart'; import 'widgets/toolbar/grid_toolbar.dart'; class GridPage extends StatefulWidget { @@ -40,7 +41,7 @@ class _GridPageState extends State { return state.loadingState.map( loading: (_) => const Center(child: CircularProgressIndicator.adaptive()), finish: (result) => result.successOrFail.fold( - (_) => const FlowyGrid(), + (_) => const GridShortcuts(child: FlowyGrid()), (err) => FlowyErrorPage(err.toString()), ), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart index 41e22b8b98..bfd0a38909 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart @@ -18,8 +18,8 @@ abstract class GridCellAccessory implements Widget { typedef AccessoryBuilder = List Function(GridCellAccessoryBuildContext buildContext); -abstract class AccessoryWidget extends Widget { - const AccessoryWidget({Key? key}) : super(key: key); +abstract class CellAccessory extends Widget { + const CellAccessory({Key? key}) : super(key: key); // The hover will show if the onFocus's value is true ValueNotifier? get isFocus; @@ -28,7 +28,7 @@ abstract class AccessoryWidget extends Widget { } class AccessoryHover extends StatefulWidget { - final AccessoryWidget child; + final CellAccessory child; final EdgeInsets contentPadding; const AccessoryHover({ required this.child, 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 05708e6198..894eed2ac8 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 @@ -8,6 +8,7 @@ import 'package:provider/provider.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:styled_widget/styled_widget.dart'; import 'cell_accessory.dart'; +import 'cell_shortcuts.dart'; import 'checkbox_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; @@ -48,7 +49,7 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget, CellContainerFocustable { +abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellFocustable, CellShortcuts { GridCellWidget({Key? key}) : super(key: key); @override @@ -58,27 +59,30 @@ abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget, List Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null; @override - final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus(); + final GridCellFocusListener beginFocus = GridCellFocusListener(); + + @override + final Map keyboardActionHandlers = {}; } abstract class GridCellState extends State { @override void initState() { - widget.requestBeginFocus.setListener(() => requestBeginFocus()); + widget.beginFocus.setListener(() => requestBeginFocus()); super.initState(); } @override void didUpdateWidget(covariant T oldWidget) { if (oldWidget != this) { - widget.requestBeginFocus.setListener(() => requestBeginFocus()); + widget.beginFocus.setListener(() => requestBeginFocus()); } super.didUpdateWidget(oldWidget); } @override void dispose() { - widget.requestBeginFocus.removeAllListener(); + widget.beginFocus.removeAllListener(); super.dispose(); } @@ -90,6 +94,7 @@ abstract class GridFocusNodeCellState extends GridCell @override void initState() { + widget.keyboardActionHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus(); _listenOnFocusNodeChanged(); super.initState(); } @@ -104,6 +109,7 @@ abstract class GridFocusNodeCellState extends GridCell @override void dispose() { + widget.keyboardActionHandlers.remove(CellKeyboardKey.onEnter); focusNode.removeAllListener(); focusNode.dispose(); super.dispose(); @@ -127,7 +133,7 @@ abstract class GridFocusNodeCellState extends GridCell Future focusChanged() async {} } -class GridCellRequestBeginFocus extends ChangeNotifier { +class GridCellFocusListener extends ChangeNotifier { VoidCallback? _listener; void setListener(VoidCallback listener) { @@ -194,9 +200,8 @@ class CellStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } -abstract class CellContainerFocustable { - // Listen on the requestBeginFocus if the - GridCellRequestBeginFocus get requestBeginFocus; +abstract class CellFocustable { + GridCellFocusListener get beginFocus; } class CellContainer extends StatelessWidget { @@ -220,7 +225,7 @@ class CellContainer extends StatelessWidget { child: Selector( selector: (context, notifier) => notifier.isFocus, builder: (context, isFocus, _) { - Widget container = Center(child: child); + Widget container = Center(child: GridCellShortcuts(child: child)); child.isFocus.addListener(() { Provider.of(context, listen: false).isFocus = child.isFocus.value; }); @@ -235,7 +240,7 @@ class CellContainer extends StatelessWidget { return GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => child.requestBeginFocus.notify(), + onTap: () => child.beginFocus.notify(), child: Container( constraints: BoxConstraints(maxWidth: width, minHeight: 46), decoration: _makeBoxDecoration(context, isFocus), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart new file mode 100644 index 0000000000..7c122f17ba --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +typedef CellKeyboardAction = VoidCallback; + +enum CellKeyboardKey { + onEnter, +} + +abstract class CellShortcuts extends Widget { + const CellShortcuts({Key? key}) : super(key: key); + + Map get keyboardActionHandlers; +} + +class GridCellShortcuts extends StatelessWidget { + final CellShortcuts child; + const GridCellShortcuts({required this.child, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Shortcuts( + shortcuts: {LogicalKeySet(LogicalKeyboardKey.enter): const GridCellEnterIdent()}, + child: Actions( + actions: {GridCellEnterIdent: GridCellEnterAction(child: child)}, + child: child, + ), + ); + } +} + +class GridCellEnterIdent extends Intent { + const GridCellEnterIdent(); +} + +class GridCellEnterAction extends Action { + final CellShortcuts child; + GridCellEnterAction({required this.child}); + + @override + void invoke(covariant GridCellEnterIdent intent) { + final callback = child.keyboardActionHandlers[CellKeyboardKey.onEnter]; + if (callback != null) { + callback(); + } + } +} 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 7e1333fff0..6a9fcde98f 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 @@ -43,6 +43,7 @@ class _GridTextCellState extends GridFocusNodeCellState { _cellBloc = getIt(param1: cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); + super.initState(); } 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 fbff4665d9..719e0082fb 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 @@ -154,7 +154,7 @@ class _RowDetailCell extends StatelessWidget { final gesture = GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => cell.requestBeginFocus.notify(), + onTap: () => cell.beginFocus.notify(), child: AccessoryHover( child: cell, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/shortcuts.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/shortcuts.dart new file mode 100644 index 0000000000..1e38c5647d --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/shortcuts.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class GridShortcuts extends StatelessWidget { + final Widget child; + const GridShortcuts({required this.child, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Shortcuts( + shortcuts: bindKeys([]), + child: Actions( + dispatcher: LoggingActionDispatcher(), + actions: const {}, + child: child, + ), + ); + } +} + +Map bindKeys(List keys) { + return {for (var key in keys) LogicalKeySet(key): KeyboardKeyIdent(key)}; +} + +Map> bindActions() { + return { + KeyboardKeyIdent: KeyboardBindingAction(), + }; +} + +class KeyboardKeyIdent extends Intent { + final KeyboardKey key; + + const KeyboardKeyIdent(this.key); +} + +class KeyboardBindingAction extends Action { + KeyboardBindingAction(); + + @override + void invoke(covariant KeyboardKeyIdent intent) { + // print(intent); + } +} + +class LoggingActionDispatcher extends ActionDispatcher { + @override + Object? invokeAction( + covariant Action action, + covariant Intent intent, [ + BuildContext? context, + ]) { + // print('Action invoked: $action($intent) from $context'); + super.invokeAction(action, intent, context); + + return null; + } +} From bb22ca5d93138b7347c54c89a94cfae808a6ee9a Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 4 Jun 2022 11:44:38 +0800 Subject: [PATCH 68/82] chore: copy cell content --- .../grid/src/widgets/cell/cell_builder.dart | 6 +- .../grid/src/widgets/cell/cell_shortcuts.dart | 59 +++++++++++++++++-- .../grid/src/widgets/cell/checkbox_cell.dart | 9 ++- .../src/widgets/cell/date_cell/date_cell.dart | 2 + .../grid/src/widgets/cell/number_cell.dart | 4 ++ .../grid/src/widgets/cell/text_cell.dart | 2 + .../src/widgets/cell/url_cell/url_cell.dart | 3 + 7 files changed, 76 insertions(+), 9 deletions(-) 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 894eed2ac8..9a1911f003 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 @@ -62,7 +62,7 @@ abstract class GridCellWidget extends StatefulWidget implements CellAccessory, C final GridCellFocusListener beginFocus = GridCellFocusListener(); @override - final Map keyboardActionHandlers = {}; + final Map shortcutHandlers = {}; } abstract class GridCellState extends State { @@ -94,7 +94,7 @@ abstract class GridFocusNodeCellState extends GridCell @override void initState() { - widget.keyboardActionHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus(); + widget.shortcutHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus(); _listenOnFocusNodeChanged(); super.initState(); } @@ -109,7 +109,7 @@ abstract class GridFocusNodeCellState extends GridCell @override void dispose() { - widget.keyboardActionHandlers.remove(CellKeyboardKey.onEnter); + widget.shortcutHandlers.clear(); focusNode.removeAllListener(); focusNode.dispose(); super.dispose(); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart index 7c122f17ba..2146c939d2 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart @@ -1,16 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -typedef CellKeyboardAction = VoidCallback; +typedef CellKeyboardAction = dynamic Function(); enum CellKeyboardKey { onEnter, + onCopy, + onInsert, } abstract class CellShortcuts extends Widget { const CellShortcuts({Key? key}) : super(key: key); - Map get keyboardActionHandlers; + Map get shortcutHandlers; } class GridCellShortcuts extends StatelessWidget { @@ -20,9 +22,17 @@ class GridCellShortcuts extends StatelessWidget { @override Widget build(BuildContext context) { return Shortcuts( - shortcuts: {LogicalKeySet(LogicalKeyboardKey.enter): const GridCellEnterIdent()}, + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.enter): const GridCellEnterIdent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyC): const GridCellCopyIntent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyV): const GridCellInsertIntent(), + }, child: Actions( - actions: {GridCellEnterIdent: GridCellEnterAction(child: child)}, + actions: { + GridCellEnterIdent: GridCellEnterAction(child: child), + GridCellCopyIntent: GridCellCopyAction(child: child), + GridCellInsertIntent: GridCellInsertAction(child: child), + }, child: child, ), ); @@ -39,7 +49,46 @@ class GridCellEnterAction extends Action { @override void invoke(covariant GridCellEnterIdent intent) { - final callback = child.keyboardActionHandlers[CellKeyboardKey.onEnter]; + final callback = child.shortcutHandlers[CellKeyboardKey.onEnter]; + if (callback != null) { + callback(); + } + } +} + +class GridCellCopyIntent extends Intent { + const GridCellCopyIntent(); +} + +class GridCellCopyAction extends Action { + final CellShortcuts child; + GridCellCopyAction({required this.child}); + + @override + void invoke(covariant GridCellCopyIntent intent) { + final callback = child.shortcutHandlers[CellKeyboardKey.onCopy]; + if (callback == null) { + return; + } + + final s = callback(); + if (s is String) { + Clipboard.setData(ClipboardData(text: s)); + } + } +} + +class GridCellInsertIntent extends Intent { + const GridCellInsertIntent(); +} + +class GridCellInsertAction extends Action { + final CellShortcuts child; + GridCellInsertAction({required this.child}); + + @override + void invoke(covariant GridCellInsertIntent intent) { + final callback = child.shortcutHandlers[CellKeyboardKey.onEnter]; if (callback != null) { callback(); } 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 6f22a7d977..21f44bf2ab 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 @@ -5,6 +5,7 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; +import 'cell_shortcuts.dart'; class CheckboxCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; @@ -24,7 +25,13 @@ class _CheckboxCellState extends GridCellState { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const CheckboxCellEvent.initial()); - + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () { + if (_cellBloc.state.isSelected) { + return "Yes"; + } else { + return "No"; + } + }; super.initState(); } 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 eed45464bf..ecc297e4c4 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 @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -45,6 +46,7 @@ class _DateCellState extends State { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const DateCellEvent.initial()); + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.dateStr; super.initState(); } 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 88aaf5cb3b..3983a7d3c3 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 @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; +import 'cell_shortcuts.dart'; class NumberCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; @@ -29,6 +30,9 @@ class _NumberCellState extends GridFocusNodeCellState { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: contentFromState(_cellBloc.state)); + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () { + return _cellBloc.state.content.fold((content) => content, (r) => null); + }; super.initState(); } 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 6a9fcde98f..48b5628bbd 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 @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'cell_builder.dart'; +import 'cell_shortcuts.dart'; class GridTextCellStyle extends GridCellStyle { String? placeholder; @@ -44,6 +45,7 @@ class _GridTextCellState extends GridFocusNodeCellState { _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.content; super.initState(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 5caa014df0..4b4e671901 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -3,6 +3,7 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -86,6 +87,8 @@ class _GridURLCellState extends GridCellState { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; _cellBloc = URLCellBloc(cellContext: cellContext); _cellBloc.add(const URLCellEvent.initial()); + + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.content; super.initState(); } From a9a75230a26c7f05ca031495f5dfccb99075e818 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sat, 4 Jun 2022 14:05:04 +0800 Subject: [PATCH 69/82] chore: insert cell content --- .../application/grid/cell/url_cell_bloc.dart | 4 ++++ .../grid/src/widgets/cell/cell_builder.dart | 14 ++++++++++++++ .../grid/src/widgets/cell/cell_shortcuts.dart | 2 +- .../grid/src/widgets/cell/checkbox_cell.dart | 17 +++++++++-------- .../src/widgets/cell/date_cell/date_cell.dart | 12 ++++++++---- .../grid/src/widgets/cell/number_cell.dart | 15 ++++++++++----- .../grid/src/widgets/cell/text_cell.dart | 11 ++++++++--- .../src/widgets/cell/url_cell/cell_editor.dart | 10 +++++++++- .../src/widgets/cell/url_cell/url_cell.dart | 18 +++++++++++++----- 9 files changed, 76 insertions(+), 27 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart index 609c625001..e1fe39c3bf 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart @@ -24,6 +24,9 @@ class URLCellBloc extends Bloc { url: cellData?.url ?? "", )); }, + updateURL: (String url) { + cellContext.saveCellData(url, deduplicate: true); + }, ); }, ); @@ -53,6 +56,7 @@ class URLCellBloc extends Bloc { @freezed class URLCellEvent with _$URLCellEvent { const factory URLCellEvent.initial() = _InitialCell; + const factory URLCellEvent.updateURL(String url) = _UpdateURL; const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate; } 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 9a1911f003..8bfab28f65 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 @@ -1,5 +1,6 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart'; import 'package:flowy_infra/theme.dart'; @@ -69,6 +70,15 @@ abstract class GridCellState extends State { @override void initState() { widget.beginFocus.setListener(() => requestBeginFocus()); + widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => onCopy(); + widget.shortcutHandlers[CellKeyboardKey.onInsert] = () { + Clipboard.getData("text/plain").then((data) { + final s = data?.text; + if (s is String) { + onInsert(s); + } + }); + }; super.initState(); } @@ -87,6 +97,10 @@ abstract class GridCellState extends State { } void requestBeginFocus(); + + String? onCopy() => null; + + void onInsert(String value) {} } abstract class GridFocusNodeCellState extends GridCellState { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart index 2146c939d2..f4f222f219 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart @@ -88,7 +88,7 @@ class GridCellInsertAction extends Action { @override void invoke(covariant GridCellInsertIntent intent) { - final callback = child.shortcutHandlers[CellKeyboardKey.onEnter]; + final callback = child.shortcutHandlers[CellKeyboardKey.onInsert]; if (callback != null) { callback(); } 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 21f44bf2ab..384d85737f 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 @@ -5,7 +5,6 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; -import 'cell_shortcuts.dart'; class CheckboxCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; @@ -25,13 +24,6 @@ class _CheckboxCellState extends GridCellState { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const CheckboxCellEvent.initial()); - widget.shortcutHandlers[CellKeyboardKey.onCopy] = () { - if (_cellBloc.state.isSelected) { - return "Yes"; - } else { - return "No"; - } - }; super.initState(); } @@ -66,4 +58,13 @@ class _CheckboxCellState extends GridCellState { void requestBeginFocus() { _cellBloc.add(const CheckboxCellEvent.select()); } + + @override + String? onCopy() { + if (_cellBloc.state.isSelected) { + return "Yes"; + } else { + return "No"; + } + } } 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 ecc297e4c4..eb382a0971 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 @@ -1,4 +1,3 @@ -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -36,17 +35,16 @@ class DateCell extends GridCellWidget { } @override - State createState() => _DateCellState(); + GridCellState createState() => _DateCellState(); } -class _DateCellState extends State { +class _DateCellState extends GridCellState { late DateCellBloc _cellBloc; @override void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const DateCellEvent.initial()); - widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.dateStr; super.initState(); } @@ -91,4 +89,10 @@ class _DateCellState extends State { _cellBloc.close(); super.dispose(); } + + @override + void requestBeginFocus() {} + + @override + String? onCopy() => _cellBloc.state.dateStr; } 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 3983a7d3c3..7d16b16ef0 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 @@ -1,12 +1,10 @@ import 'dart:async'; - 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'; import 'cell_builder.dart'; -import 'cell_shortcuts.dart'; class NumberCell extends GridCellWidget { final GridCellContextBuilder cellContextBuilder; @@ -30,9 +28,6 @@ class _NumberCellState extends GridFocusNodeCellState { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: contentFromState(_cellBloc.state)); - widget.shortcutHandlers[CellKeyboardKey.onCopy] = () { - return _cellBloc.state.content.fold((content) => content, (r) => null); - }; super.initState(); } @@ -85,4 +80,14 @@ class _NumberCellState extends GridFocusNodeCellState { String contentFromState(NumberCellState state) { return state.content.fold((l) => l, (r) => ""); } + + @override + String? onCopy() { + return _cellBloc.state.content.fold((content) => content, (r) => null); + } + + @override + void onInsert(String value) { + _cellBloc.add(NumberCellEvent.updateCell(value)); + } } 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 48b5628bbd..1bece5a3d7 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 @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'cell_builder.dart'; -import 'cell_shortcuts.dart'; class GridTextCellStyle extends GridCellStyle { String? placeholder; @@ -44,8 +43,6 @@ class _GridTextCellState extends GridFocusNodeCellState { _cellBloc = getIt(param1: cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - - widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.content; super.initState(); } @@ -95,4 +92,12 @@ class _GridTextCellState extends GridFocusNodeCellState { }); } } + + @override + String? onCopy() => _cellBloc.state.content; + + @override + void onInsert(String value) { + _cellBloc.add(TextCellEvent.updateText(value)); + } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart index 15af124749..f4da18be86 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart @@ -8,7 +8,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { final GridURLCellContext cellContext; - const URLCellEditor({required this.cellContext, Key? key}) : super(key: key); + final VoidCallback completed; + const URLCellEditor({required this.cellContext, required this.completed, Key? key}) : super(key: key); @override State createState() => _URLCellEditorState(); @@ -16,10 +17,12 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { static void show( BuildContext context, GridURLCellContext cellContext, + VoidCallback completed, ) { FlowyOverlay.of(context).remove(identifier()); final editor = URLCellEditor( cellContext: cellContext, + completed: completed, ); // @@ -46,6 +49,11 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { bool asBarrier() { return true; } + + @override + void didRemove() { + completed(); + } } class _URLCellEditorState extends State { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 4b4e671901..b3e9c3c919 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -3,7 +3,6 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart'; import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -87,8 +86,6 @@ class _GridURLCellState extends GridCellState { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; _cellBloc = URLCellBloc(cellContext: cellContext); _cellBloc.add(const URLCellEvent.initial()); - - widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => _cellBloc.state.content; super.initState(); } @@ -134,10 +131,13 @@ class _GridURLCellState extends GridCellState { Future _openUrlOrEdit(String url) async { final uri = Uri.parse(url); if (url.isNotEmpty && await canLaunchUrl(uri)) { + widget.isFocus.value = false; await launchUrl(uri); } else { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; - URLCellEditor.show(context, cellContext); + URLCellEditor.show(context, cellContext, () { + widget.isFocus.value = false; + }); } } @@ -145,6 +145,14 @@ class _GridURLCellState extends GridCellState { void requestBeginFocus() { _openUrlOrEdit(_cellBloc.state.url); } + + @override + String? onCopy() => _cellBloc.state.content; + + @override + void onInsert(String value) { + _cellBloc.add(URLCellEvent.updateURL(value)); + } } class _EditURLAccessory extends StatelessWidget with GridCellAccessory { @@ -164,7 +172,7 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory { @override void onTap() { - URLCellEditor.show(anchorContext, cellContext); + URLCellEditor.show(anchorContext, cellContext, () {}); } } From f61bebe869c0bbe7fe9cee72b8e948d58c23264d Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 5 Jun 2022 18:39:37 +0800 Subject: [PATCH 70/82] chore: hide accessory when cell is editing --- .../grid/src/widgets/cell/cell_accessory.dart | 71 +++++--- .../grid/src/widgets/cell/cell_builder.dart | 152 +++--------------- .../grid/src/widgets/cell/cell_cotainer.dart | 140 ++++++++++++++++ .../src/widgets/cell/date_cell/date_cell.dart | 4 +- .../select_option_cell.dart | 4 +- .../src/widgets/cell/url_cell/url_cell.dart | 5 +- .../grid/src/widgets/row/grid_row.dart | 28 +--- 7 files changed, 225 insertions(+), 179 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_cotainer.dart diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart index bfd0a38909..88eadb39c4 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart @@ -1,3 +1,4 @@ +import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/widgets.dart'; import 'package:flowy_infra/theme.dart'; @@ -8,12 +9,45 @@ import 'package:styled_widget/styled_widget.dart'; class GridCellAccessoryBuildContext { final BuildContext anchorContext; + final bool isCellEditing; - GridCellAccessoryBuildContext({required this.anchorContext}); + GridCellAccessoryBuildContext({ + required this.anchorContext, + required this.isCellEditing, + }); } abstract class GridCellAccessory implements Widget { void onTap(); + + // The accessory will be hidden if enable() return false; + bool enable() => true; +} + +class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory { + final VoidCallback onTapCallback; + final bool isCellEditing; + const PrimaryCellAccessory({ + required this.onTapCallback, + required this.isCellEditing, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (isCellEditing) { + return const SizedBox(); + } else { + final theme = context.watch(); + return svgWidget("grid/expander", color: theme.main1); + } + } + + @override + void onTap() => onTapCallback(); + + @override + bool enable() => !isCellEditing; } typedef AccessoryBuilder = List Function(GridCellAccessoryBuildContext buildContext); @@ -21,8 +55,8 @@ typedef AccessoryBuilder = List Function(GridCellAccessoryBui abstract class CellAccessory extends Widget { const CellAccessory({Key? key}) : super(key: key); - // The hover will show if the onFocus's value is true - ValueNotifier? get isFocus; + // The hover will show if the isHover's value is true + ValueNotifier? get onAccessoryHover; AccessoryBuilder? get accessoryBuilder; } @@ -47,8 +81,8 @@ class _AccessoryHoverState extends State { @override void initState() { _hoverState = AccessoryHoverState(); - _listenerFn = () => _hoverState.isFocus = widget.child.isFocus?.value ?? false; - widget.child.isFocus?.addListener(_listenerFn!); + _listenerFn = () => _hoverState.onHover = widget.child.onAccessoryHover?.value ?? false; + widget.child.onAccessoryHover?.addListener(_listenerFn!); super.initState(); } @@ -58,7 +92,7 @@ class _AccessoryHoverState extends State { _hoverState.dispose(); if (_listenerFn != null) { - widget.child.isFocus?.removeListener(_listenerFn!); + widget.child.onAccessoryHover?.removeListener(_listenerFn!); _listenerFn = null; } super.dispose(); @@ -73,11 +107,14 @@ class _AccessoryHoverState extends State { final accessoryBuilder = widget.child.accessoryBuilder; if (accessoryBuilder != null) { - final accessories = accessoryBuilder((GridCellAccessoryBuildContext(anchorContext: context))); + final accessories = accessoryBuilder((GridCellAccessoryBuildContext( + anchorContext: context, + isCellEditing: false, + ))); children.add( Padding( padding: const EdgeInsets.only(right: 6), - child: AccessoryContainer(accessories: accessories), + child: CellAccessoryContainer(accessories: accessories), ).positioned(right: 0), ); } @@ -101,7 +138,6 @@ class _AccessoryHoverState extends State { class AccessoryHoverState extends ChangeNotifier { bool _onHover = false; - bool _isFocus = false; set onHover(bool value) { if (_onHover != value) { @@ -111,15 +147,6 @@ class AccessoryHoverState extends ChangeNotifier { } bool get onHover => _onHover; - - set isFocus(bool value) { - if (_isFocus != value) { - _isFocus = value; - notifyListeners(); - } - } - - bool get isFocus => _isFocus; } class _Background extends StatelessWidget { @@ -130,7 +157,7 @@ class _Background extends StatelessWidget { final theme = context.watch(); return Consumer( builder: (context, state, child) { - if (state.onHover || state.isFocus) { + if (state.onHover) { return FlowyHoverContainer( style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), ); @@ -142,14 +169,14 @@ class _Background extends StatelessWidget { } } -class AccessoryContainer extends StatelessWidget { +class CellAccessoryContainer extends StatelessWidget { final List accessories; - const AccessoryContainer({required this.accessories, Key? key}) : super(key: key); + const CellAccessoryContainer({required this.accessories, Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); - final children = accessories.map((accessory) { + final children = accessories.where((accessory) => accessory.enable()).map((accessory) { final hover = FlowyHover( style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface), builder: (_, onHover) => Container( 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 8bfab28f65..7147272038 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 @@ -2,12 +2,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_serv import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart'; -import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -import 'package:styled_widget/styled_widget.dart'; import 'cell_accessory.dart'; import 'cell_shortcuts.dart'; import 'checkbox_cell.dart'; @@ -50,11 +45,30 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellFocustable, CellShortcuts { - GridCellWidget({Key? key}) : super(key: key); +abstract class CellEditable { + GridCellFocusListener get beginFocus; + + ValueNotifier get onCellFocus; + + ValueNotifier get onCellEditing; +} + +abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellEditable, CellShortcuts { + GridCellWidget({Key? key}) : super(key: key) { + onCellEditing.addListener(() { + onCellFocus.value = onCellEditing.value; + }); + } @override - final ValueNotifier isFocus = ValueNotifier(false); + final ValueNotifier onCellFocus = ValueNotifier(false); + + // When the cell is focused, we assume that the accessory alse be hovered. + @override + ValueNotifier get onAccessoryHover => onCellFocus; + + @override + final ValueNotifier onCellEditing = ValueNotifier(false); @override List Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null; @@ -137,9 +151,9 @@ abstract class GridFocusNodeCellState extends GridCell } void _listenOnFocusNodeChanged() { - widget.isFocus.value = focusNode.hasFocus; + widget.onCellEditing.value = focusNode.hasFocus; focusNode.setListener(() { - widget.isFocus.value = focusNode.hasFocus; + widget.onCellEditing.value = focusNode.hasFocus; focusChanged(); }); } @@ -190,121 +204,3 @@ class SingleListenrFocusNode extends FocusNode { } } } - -class CellStateNotifier extends ChangeNotifier { - bool _isFocus = false; - bool _onEnter = false; - - set isFocus(bool value) { - if (_isFocus != value) { - _isFocus = value; - notifyListeners(); - } - } - - set onEnter(bool value) { - if (_onEnter != value) { - _onEnter = value; - notifyListeners(); - } - } - - bool get isFocus => _isFocus; - - bool get onEnter => _onEnter; -} - -abstract class CellFocustable { - GridCellFocusListener get beginFocus; -} - -class CellContainer extends StatelessWidget { - final GridCellWidget child; - final AccessoryBuilder? accessoryBuilder; - final double width; - final RegionStateNotifier rowStateNotifier; - const CellContainer({ - Key? key, - required this.child, - required this.width, - required this.rowStateNotifier, - this.accessoryBuilder, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ChangeNotifierProxyProvider( - create: (_) => CellStateNotifier(), - update: (_, row, cell) => cell!..onEnter = row.onEnter, - child: Selector( - selector: (context, notifier) => notifier.isFocus, - builder: (context, isFocus, _) { - Widget container = Center(child: GridCellShortcuts(child: child)); - child.isFocus.addListener(() { - Provider.of(context, listen: false).isFocus = child.isFocus.value; - }); - - if (accessoryBuilder != null) { - final buildContext = GridCellAccessoryBuildContext(anchorContext: context); - final accessories = accessoryBuilder!(buildContext); - if (accessories.isNotEmpty) { - container = CellEnterRegion(child: container, accessories: accessories); - } - } - - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => child.beginFocus.notify(), - child: Container( - constraints: BoxConstraints(maxWidth: width, minHeight: 46), - decoration: _makeBoxDecoration(context, isFocus), - padding: GridSize.cellContentInsets, - child: container, - ), - ); - }, - ), - ); - } - - BoxDecoration _makeBoxDecoration(BuildContext context, bool isFocus) { - final theme = context.watch(); - if (isFocus) { - final borderSide = BorderSide(color: theme.main1, width: 1.0); - return BoxDecoration(border: Border.fromBorderSide(borderSide)); - } else { - final borderSide = BorderSide(color: theme.shader5, width: 1.0); - return BoxDecoration(border: Border(right: borderSide, bottom: borderSide)); - } - } -} - -class CellEnterRegion extends StatelessWidget { - final Widget child; - final List accessories; - const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (context, notifier) => notifier.onEnter, - builder: (context, onEnter, _) { - List children = [child]; - if (onEnter) { - children.add(AccessoryContainer(accessories: accessories).positioned(right: 0)); - } - - return MouseRegion( - cursor: SystemMouseCursors.click, - onEnter: (p) => Provider.of(context, listen: false).onEnter = true, - onExit: (p) => Provider.of(context, listen: false).onEnter = false, - child: Stack( - alignment: AlignmentDirectional.center, - fit: StackFit.expand, - children: children, - ), - ); - }, - ); - } -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_cotainer.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_cotainer.dart new file mode 100644 index 0000000000..fbb343dd6c --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_cotainer.dart @@ -0,0 +1,140 @@ +import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; +import 'package:styled_widget/styled_widget.dart'; + +import 'cell_accessory.dart'; +import 'cell_builder.dart'; +import 'cell_shortcuts.dart'; + +class CellContainer extends StatelessWidget { + final GridCellWidget child; + final AccessoryBuilder? accessoryBuilder; + final double width; + final RegionStateNotifier rowStateNotifier; + const CellContainer({ + Key? key, + required this.child, + required this.width, + required this.rowStateNotifier, + this.accessoryBuilder, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProxyProvider( + create: (_) => CellContainerNotifier(child), + update: (_, rowStateNotifier, cellStateNotifier) => cellStateNotifier!..onEnter = rowStateNotifier.onEnter, + child: Selector( + selector: (context, notifier) => notifier.isFocus, + builder: (context, isFocus, _) { + Widget container = Center(child: GridCellShortcuts(child: child)); + + if (accessoryBuilder != null) { + final accessories = accessoryBuilder!(GridCellAccessoryBuildContext( + anchorContext: context, + isCellEditing: isFocus, + )); + + if (accessories.isNotEmpty) { + container = CellEnterRegion(child: container, accessories: accessories); + } + } + + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => child.beginFocus.notify(), + child: Container( + constraints: BoxConstraints(maxWidth: width, minHeight: 46), + decoration: _makeBoxDecoration(context, isFocus), + padding: GridSize.cellContentInsets, + child: container, + ), + ); + }, + ), + ); + } + + BoxDecoration _makeBoxDecoration(BuildContext context, bool isFocus) { + final theme = context.watch(); + if (isFocus) { + final borderSide = BorderSide(color: theme.main1, width: 1.0); + return BoxDecoration(border: Border.fromBorderSide(borderSide)); + } else { + final borderSide = BorderSide(color: theme.shader5, width: 1.0); + return BoxDecoration(border: Border(right: borderSide, bottom: borderSide)); + } + } +} + +class CellEnterRegion extends StatelessWidget { + final Widget child; + final List accessories; + const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (context, notifier) => notifier.onEnter, + builder: (context, onEnter, _) { + List children = [child]; + if (onEnter) { + children.add(CellAccessoryContainer(accessories: accessories).positioned(right: 0)); + } + + return MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (p) => Provider.of(context, listen: false).onEnter = true, + onExit: (p) => Provider.of(context, listen: false).onEnter = false, + child: Stack( + alignment: AlignmentDirectional.center, + fit: StackFit.expand, + children: children, + ), + ); + }, + ); + } +} + +class CellContainerNotifier extends ChangeNotifier { + final CellEditable cellEditable; + bool mouted = false; + VoidCallback? _onCellFocusListener; + bool _isFocus = false; + bool _onEnter = false; + + CellContainerNotifier(this.cellEditable) { + _onCellFocusListener = () => isFocus = cellEditable.onCellFocus.value; + cellEditable.onCellFocus.addListener(_onCellFocusListener!); + } + + @override + void dispose() { + if (_onCellFocusListener != null) { + cellEditable.onCellFocus.removeListener(_onCellFocusListener!); + } + super.dispose(); + } + + set isFocus(bool value) { + if (_isFocus != value) { + _isFocus = value; + notifyListeners(); + } + } + + set onEnter(bool value) { + if (_onEnter != value) { + _onEnter = value; + notifyListeners(); + } + } + + bool get isFocus => _isFocus; + + bool get onEnter => _onEnter; +} 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 eb382a0971..3e7d40c796 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 @@ -76,8 +76,8 @@ class _DateCellState extends GridCellState { void _showCalendar(BuildContext context) { final bloc = context.read(); - widget.isFocus.value = true; - final calendar = DateCellEditor(onDismissed: () => widget.isFocus.value = false); + widget.onCellEditing.value = true; + final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false); calendar.show( context, cellContext: bloc.cellContext.clone(), 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 294a87976f..e878ac2c58 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 @@ -59,7 +59,7 @@ class _SingleSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.isFocus.value = value, + onFocus: (value) => widget.onCellEditing.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), @@ -113,7 +113,7 @@ class _MultiSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.isFocus.value = value, + onFocus: (value) => widget.onCellEditing.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index b3e9c3c919..e37dca6632 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -112,7 +112,6 @@ class _GridURLCellState extends GridCellState { child: GestureDetector( child: Align(alignment: Alignment.centerLeft, child: richText), onTap: () async { - widget.isFocus.value = true; final url = context.read().state.url; await _openUrlOrEdit(url); }, @@ -131,12 +130,12 @@ class _GridURLCellState extends GridCellState { Future _openUrlOrEdit(String url) async { final uri = Uri.parse(url); if (url.isNotEmpty && await canLaunchUrl(uri)) { - widget.isFocus.value = false; await launchUrl(uri); } else { final cellContext = widget.cellContextBuilder.build() as GridURLCellContext; + widget.onCellEditing.value = true; URLCellEditor.show(context, cellContext, () { - widget.isFocus.value = false; + widget.onCellEditing.value = false; }); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index b646af6c40..15dbb0bfc7 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -1,6 +1,7 @@ import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_cotainer.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -172,16 +173,15 @@ class _RowCells extends StatelessWidget { return gridCellMap.values.map( (gridCell) { final GridCellWidget child = buildGridCellWidget(gridCell, cellCache); - List accessories = []; - if (gridCell.field.isPrimary) { - accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); - } - accessoryBuilder(buildContext) { + accessoryBuilder(GridCellAccessoryBuildContext buildContext) { final builder = child.accessoryBuilder; List accessories = []; if (gridCell.field.isPrimary) { - accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); + accessories.add(PrimaryCellAccessory( + onTapCallback: onExpand, + isCellEditing: buildContext.isCellEditing, + )); } if (builder != null) { @@ -214,22 +214,6 @@ class RegionStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } -class _PrimaryCellAccessory extends StatelessWidget with GridCellAccessory { - final VoidCallback onTapCallback; - const _PrimaryCellAccessory({required this.onTapCallback, Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final theme = context.watch(); - return svgWidget("grid/expander", color: theme.main1); - } - - @override - void onTap() { - onTapCallback(); - } -} - class _RowEnterRegion extends StatefulWidget { final Widget child; const _RowEnterRegion({required this.child, Key? key}) : super(key: key); From 2dfb7261c7cdd111314440933de1f8660a21c114 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 5 Jun 2022 18:56:45 +0800 Subject: [PATCH 71/82] chore: rename some struct in Rust --- .../application/doc/doc_service.dart | 4 +-- .../application/doc/share_service.dart | 2 +- .../flowy-text-block/dart_event.dart | 18 +++++----- .../flowy-text-block/event_map.pbenum.dart | 16 ++++----- .../flowy-text-block/event_map.pbjson.dart | 10 +++--- .../flowy-text-block/src/event_map.rs | 8 ++--- .../src/protobuf/model/event_map.rs | 36 +++++++++---------- .../src/protobuf/proto/event_map.proto | 2 +- 8 files changed, 48 insertions(+), 48 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/doc/doc_service.dart b/frontend/app_flowy/lib/workspace/application/doc/doc_service.dart index a0498491f9..6ad08814f4 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/doc_service.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/doc_service.dart @@ -12,14 +12,14 @@ class DocumentService { await FolderEventSetLatestView(ViewId(value: docId)).send(); final payload = TextBlockId(value: docId); - return BlockEventGetBlockData(payload).send(); + return TextBlockEventGetBlockData(payload).send(); } Future> composeDelta({required String docId, required String data}) { final payload = TextBlockDelta.create() ..blockId = docId ..deltaStr = data; - return BlockEventApplyDelta(payload).send(); + return TextBlockEventApplyDelta(payload).send(); } Future> closeDocument({required String docId}) { diff --git a/frontend/app_flowy/lib/workspace/application/doc/share_service.dart b/frontend/app_flowy/lib/workspace/application/doc/share_service.dart index cc3afe1314..7e5545f109 100644 --- a/frontend/app_flowy/lib/workspace/application/doc/share_service.dart +++ b/frontend/app_flowy/lib/workspace/application/doc/share_service.dart @@ -10,7 +10,7 @@ class ShareService { ..viewId = docId ..exportType = type; - return BlockEventExportDocument(request).send(); + return TextBlockEventExportDocument(request).send(); } Future> exportText(String docId) { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-text-block/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-text-block/dart_event.dart index 6785a1c681..6b9df7ae97 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-text-block/dart_event.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-text-block/dart_event.dart @@ -1,13 +1,13 @@ /// Auto generate. Do not edit part of '../../dispatch.dart'; -class BlockEventGetBlockData { +class TextBlockEventGetBlockData { TextBlockId request; - BlockEventGetBlockData(this.request); + TextBlockEventGetBlockData(this.request); Future> send() { final request = FFIRequest.create() - ..event = BlockEvent.GetBlockData.toString() + ..event = TextBlockEvent.GetBlockData.toString() ..payload = requestToBytes(this.request); return Dispatch.asyncRequest(request) @@ -18,13 +18,13 @@ class BlockEventGetBlockData { } } -class BlockEventApplyDelta { +class TextBlockEventApplyDelta { TextBlockDelta request; - BlockEventApplyDelta(this.request); + TextBlockEventApplyDelta(this.request); Future> send() { final request = FFIRequest.create() - ..event = BlockEvent.ApplyDelta.toString() + ..event = TextBlockEvent.ApplyDelta.toString() ..payload = requestToBytes(this.request); return Dispatch.asyncRequest(request) @@ -35,13 +35,13 @@ class BlockEventApplyDelta { } } -class BlockEventExportDocument { +class TextBlockEventExportDocument { ExportPayload request; - BlockEventExportDocument(this.request); + TextBlockEventExportDocument(this.request); Future> send() { final request = FFIRequest.create() - ..event = BlockEvent.ExportDocument.toString() + ..event = TextBlockEvent.ExportDocument.toString() ..payload = requestToBytes(this.request); return Dispatch.asyncRequest(request) diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbenum.dart index d88c52395c..02414f63fe 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbenum.dart @@ -9,20 +9,20 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -class BlockEvent extends $pb.ProtobufEnum { - static const BlockEvent GetBlockData = BlockEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetBlockData'); - static const BlockEvent ApplyDelta = BlockEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDelta'); - static const BlockEvent ExportDocument = BlockEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ExportDocument'); +class TextBlockEvent extends $pb.ProtobufEnum { + static const TextBlockEvent GetBlockData = TextBlockEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetBlockData'); + static const TextBlockEvent ApplyDelta = TextBlockEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDelta'); + static const TextBlockEvent ExportDocument = TextBlockEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ExportDocument'); - static const $core.List values = [ + static const $core.List values = [ GetBlockData, ApplyDelta, ExportDocument, ]; - static final $core.Map<$core.int, BlockEvent> _byValue = $pb.ProtobufEnum.initByValue(values); - static BlockEvent? valueOf($core.int value) => _byValue[value]; + static final $core.Map<$core.int, TextBlockEvent> _byValue = $pb.ProtobufEnum.initByValue(values); + static TextBlockEvent? valueOf($core.int value) => _byValue[value]; - const BlockEvent._($core.int v, $core.String n) : super(v, n); + const TextBlockEvent._($core.int v, $core.String n) : super(v, n); } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbjson.dart index ac0f243e6f..f4c13be996 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-text-block/event_map.pbjson.dart @@ -8,9 +8,9 @@ import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; -@$core.Deprecated('Use blockEventDescriptor instead') -const BlockEvent$json = const { - '1': 'BlockEvent', +@$core.Deprecated('Use textBlockEventDescriptor instead') +const TextBlockEvent$json = const { + '1': 'TextBlockEvent', '2': const [ const {'1': 'GetBlockData', '2': 0}, const {'1': 'ApplyDelta', '2': 1}, @@ -18,5 +18,5 @@ const BlockEvent$json = const { ], }; -/// Descriptor for `BlockEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List blockEventDescriptor = $convert.base64Decode('CgpCbG9ja0V2ZW50EhAKDEdldEJsb2NrRGF0YRAAEg4KCkFwcGx5RGVsdGEQARISCg5FeHBvcnREb2N1bWVudBAC'); +/// Descriptor for `TextBlockEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List textBlockEventDescriptor = $convert.base64Decode('Cg5UZXh0QmxvY2tFdmVudBIQCgxHZXRCbG9ja0RhdGEQABIOCgpBcHBseURlbHRhEAESEgoORXhwb3J0RG9jdW1lbnQQAg=='); diff --git a/frontend/rust-lib/flowy-text-block/src/event_map.rs b/frontend/rust-lib/flowy-text-block/src/event_map.rs index a355af8bc3..f995fd282b 100644 --- a/frontend/rust-lib/flowy-text-block/src/event_map.rs +++ b/frontend/rust-lib/flowy-text-block/src/event_map.rs @@ -9,16 +9,16 @@ pub fn create(block_manager: Arc) -> Module { let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(block_manager); module = module - .event(BlockEvent::GetBlockData, get_block_data_handler) - .event(BlockEvent::ApplyDelta, apply_delta_handler) - .event(BlockEvent::ExportDocument, export_handler); + .event(TextBlockEvent::GetBlockData, get_block_data_handler) + .event(TextBlockEvent::ApplyDelta, apply_delta_handler) + .event(TextBlockEvent::ExportDocument, export_handler); module } #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] #[event_err = "FlowyError"] -pub enum BlockEvent { +pub enum TextBlockEvent { #[event(input = "TextBlockId", output = "TextBlockDelta")] GetBlockData = 0, diff --git a/frontend/rust-lib/flowy-text-block/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-text-block/src/protobuf/model/event_map.rs index ab1a5da855..f0356559c5 100644 --- a/frontend/rust-lib/flowy-text-block/src/protobuf/model/event_map.rs +++ b/frontend/rust-lib/flowy-text-block/src/protobuf/model/event_map.rs @@ -24,31 +24,31 @@ // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2; #[derive(Clone,PartialEq,Eq,Debug,Hash)] -pub enum BlockEvent { +pub enum TextBlockEvent { GetBlockData = 0, ApplyDelta = 1, ExportDocument = 2, } -impl ::protobuf::ProtobufEnum for BlockEvent { +impl ::protobuf::ProtobufEnum for TextBlockEvent { fn value(&self) -> i32 { *self as i32 } - fn from_i32(value: i32) -> ::std::option::Option { + fn from_i32(value: i32) -> ::std::option::Option { match value { - 0 => ::std::option::Option::Some(BlockEvent::GetBlockData), - 1 => ::std::option::Option::Some(BlockEvent::ApplyDelta), - 2 => ::std::option::Option::Some(BlockEvent::ExportDocument), + 0 => ::std::option::Option::Some(TextBlockEvent::GetBlockData), + 1 => ::std::option::Option::Some(TextBlockEvent::ApplyDelta), + 2 => ::std::option::Option::Some(TextBlockEvent::ExportDocument), _ => ::std::option::Option::None } } fn values() -> &'static [Self] { - static values: &'static [BlockEvent] = &[ - BlockEvent::GetBlockData, - BlockEvent::ApplyDelta, - BlockEvent::ExportDocument, + static values: &'static [TextBlockEvent] = &[ + TextBlockEvent::GetBlockData, + TextBlockEvent::ApplyDelta, + TextBlockEvent::ExportDocument, ]; values } @@ -56,30 +56,30 @@ impl ::protobuf::ProtobufEnum for BlockEvent { fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { - ::protobuf::reflect::EnumDescriptor::new_pb_name::("BlockEvent", file_descriptor_proto()) + ::protobuf::reflect::EnumDescriptor::new_pb_name::("TextBlockEvent", file_descriptor_proto()) }) } } -impl ::std::marker::Copy for BlockEvent { +impl ::std::marker::Copy for TextBlockEvent { } -impl ::std::default::Default for BlockEvent { +impl ::std::default::Default for TextBlockEvent { fn default() -> Self { - BlockEvent::GetBlockData + TextBlockEvent::GetBlockData } } -impl ::protobuf::reflect::ProtobufValue for BlockEvent { +impl ::protobuf::reflect::ProtobufValue for TextBlockEvent { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self)) } } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fevent_map.proto*B\n\nBlockEvent\x12\x10\n\x0cGetBlockData\x10\0\ - \x12\x0e\n\nApplyDelta\x10\x01\x12\x12\n\x0eExportDocument\x10\x02b\x06p\ - roto3\ + \n\x0fevent_map.proto*F\n\x0eTextBlockEvent\x12\x10\n\x0cGetBlockData\ + \x10\0\x12\x0e\n\nApplyDelta\x10\x01\x12\x12\n\x0eExportDocument\x10\x02\ + b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-text-block/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-text-block/src/protobuf/proto/event_map.proto index 3ebf0755d3..f7e088c938 100644 --- a/frontend/rust-lib/flowy-text-block/src/protobuf/proto/event_map.proto +++ b/frontend/rust-lib/flowy-text-block/src/protobuf/proto/event_map.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -enum BlockEvent { +enum TextBlockEvent { GetBlockData = 0; ApplyDelta = 1; ExportDocument = 2; From 39ee73cb641aecf76267c35c31a8495f043855ea Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 5 Jun 2022 21:02:51 +0800 Subject: [PATCH 72/82] fix: emoji picker remove without attach overlay --- .../widgets/emoji_picker/src/emoji_button.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart index 8866b56d5b..de8434a1dd 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart @@ -27,7 +27,7 @@ class _EmojiStyleButtonState extends State { bool _isToggled = false; // Style get _selectionStyle => widget.controller.getSelectionStyle(); final GlobalKey emojiButtonKey = GlobalKey(); - OverlayEntry _entry = OverlayEntry(builder: (context) => Container()); + OverlayEntry? _entry; // final FocusNode _keyFocusNode = FocusNode(); @override @@ -54,7 +54,7 @@ class _EmojiStyleButtonState extends State { @override void dispose() { - _entry.remove(); + _entry?.remove(); super.dispose(); } @@ -83,8 +83,9 @@ class _EmojiStyleButtonState extends State { // } void _toggleAttribute() { - if (_entry.mounted) { - _entry.remove(); + if (_entry?.mounted ?? false) { + _entry?.remove(); + _entry = null; setState(() => _isToggled = false); } else { RenderBox box = emojiButtonKey.currentContext?.findRenderObject() as RenderBox; @@ -99,7 +100,7 @@ class _EmojiStyleButtonState extends State { ), ); - Overlay.of(context)!.insert(_entry); + Overlay.of(context)!.insert(_entry!); setState(() => _isToggled = true); } From 6bf1d9c19a3772c244f14c9aee3c021a72b9ad40 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 5 Jun 2022 22:28:15 +0800 Subject: [PATCH 73/82] chore: fix grid scollbar not visible after screen size changed --- .../app_flowy/lib/workspace/presentation/home/home_screen.dart | 2 -- .../app_flowy/lib/workspace/presentation/home/home_stack.dart | 2 -- 2 files changed, 4 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart index 86fa9fab79..7e6d4abba2 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart @@ -18,7 +18,6 @@ import 'home_stack.dart'; import 'menu/menu.dart'; class HomeScreen extends StatefulWidget { - static GlobalKey scaffoldKey = GlobalKey(); final UserProfile user; final CurrentWorkspaceSetting workspaceSetting; const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key); @@ -52,7 +51,6 @@ class _HomeScreenState extends State { ), ], child: Scaffold( - key: HomeScreen.scaffoldKey, body: BlocListener( listenWhen: (p, c) => p.unauthorized != c.unauthorized, listener: (context, state) { diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart index c9c210ab35..07bd99deb1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart @@ -21,8 +21,6 @@ import 'package:flowy_infra/notifier.dart'; typedef NavigationCallback = void Function(String id); class HomeStack extends StatelessWidget { - static GlobalKey scaffoldKey = GlobalKey(); - // final Size size; const HomeStack({Key? key}) : super(key: key); @override From ad5ab10cdd7ce92b139e6e3d147526bf67a98c5e Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 5 Jun 2022 22:29:09 +0800 Subject: [PATCH 74/82] chore: update default scrollbar size --- .../lib/style_widget/scrolling/styled_scrollview.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scrollview.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scrollview.dart index da316685dd..bf086f756e 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scrollview.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/scrolling/styled_scrollview.dart @@ -23,7 +23,7 @@ class StyledSingleChildScrollView extends StatefulWidget { this.handleColor, this.controller, this.scrollbarPadding, - this.barSize = 6, + this.barSize = 12, }) : super(key: key); @override From 8fd9b68d9d3b5fc5d47ebffa0fe91298b0ec311b Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 6 Jun 2022 20:06:08 +0800 Subject: [PATCH 75/82] fix: duplicate grid --- .../home/menu/app/header/add_button.dart | 2 +- frontend/rust-lib/Cargo.lock | 1 + frontend/rust-lib/flowy-folder/src/manager.rs | 4 +-- .../src/services/view/controller.rs | 6 ++-- frontend/rust-lib/flowy-grid/Cargo.toml | 1 + frontend/rust-lib/flowy-grid/src/manager.rs | 31 ++++++++++-------- .../src/services/block_meta_editor.rs | 6 +++- .../src/services/block_meta_manager.rs | 3 +- .../number_type_option/number_type_option.rs | 8 +++-- .../flowy-grid/src/services/grid_editor.rs | 29 +++++++++++++++++ .../flowy-sdk/src/deps_resolve/folder_deps.rs | 12 +++---- .../src/entities/meta.rs | 28 ++++++---------- .../src/client_grid/grid_block_meta_pad.rs | 21 +++++++++++- .../src/client_grid/grid_builder.rs | 32 +++++++++++++++---- .../src/client_grid/grid_meta_pad.rs | 26 +++++++++++++-- 15 files changed, 150 insertions(+), 60 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart index a015930a3e..39c13c1c12 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart @@ -46,8 +46,8 @@ class ActionList { return CreateItem( pluginBuilder: pluginBuilder, onSelected: (builder) { - FlowyOverlay.of(buildContext).remove(_identifier); onSelected(builder); + FlowyOverlay.of(buildContext).remove(_identifier); }, ); }, diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 393a02ac73..57cfede8cb 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "flowy-revision", "flowy-sync", "flowy-test", + "futures", "indexmap", "lazy_static", "lib-dispatch", diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index fe9a160b39..3181c14625 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -241,11 +241,11 @@ pub trait ViewDataProcessor { fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>; - fn delta_bytes(&self, view_id: &str) -> FutureResult; + fn view_delta_data(&self, view_id: &str) -> FutureResult; fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult; - fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec) -> FutureResult; + fn process_view_delta_data(&self, user_id: &str, view_id: &str, data: Vec) -> FutureResult; fn data_type(&self) -> ViewDataType; } diff --git a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs index ae88073afc..4de3b306d8 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs @@ -60,7 +60,7 @@ impl ViewController { params.data = view_data.to_vec(); } else { let delta_data = processor - .process_create_view_data(&user_id, ¶ms.view_id, params.data.clone()) + .process_view_delta_data(&user_id, ¶ms.view_id, params.data.clone()) .await?; let _ = self .create_view(¶ms.view_id, params.data_type.clone(), delta_data) @@ -176,7 +176,7 @@ impl ViewController { .await?; let processor = self.get_data_processor(&view.data_type)?; - let delta_bytes = processor.delta_bytes(view_id).await?; + let delta_bytes = processor.view_delta_data(view_id).await?; let duplicate_params = CreateViewParams { belong_to_id: view.belong_to_id.clone(), name: format!("{} (copy)", &view.name), @@ -238,7 +238,7 @@ impl ViewController { } impl ViewController { - #[tracing::instrument(level = "debug", skip(self), err)] + #[tracing::instrument(level = "debug", skip(self, params), err)] async fn create_view_on_server(&self, params: CreateViewParams) -> Result { let token = self.user.token()?; let view = self.cloud_service.create_view(&token, params).await?; diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index 43b0cbf69f..1cf2ee0e53 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -37,6 +37,7 @@ serde_repr = "0.1" indexmap = {version = "1.8.1", features = ["serde"]} fancy-regex = "0.10.0" url = { version = "2"} +futures = "0.3.15" [dev-dependencies] flowy-test = { path = "../flowy-test" } diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index f8ea9e70ae..2965cb5bd9 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -154,11 +154,10 @@ pub async fn make_grid_view_data( grid_manager: Arc, build_context: BuildGridContext, ) -> FlowyResult { - let block_id = build_context.block_meta.block_id.clone(); let grid_meta = GridMeta { grid_id: view_id.to_string(), fields: build_context.field_metas, - blocks: vec![build_context.block_meta], + blocks: build_context.blocks, }; // Create grid @@ -168,19 +167,23 @@ pub async fn make_grid_view_data( Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into(); let _ = grid_manager.create_grid(view_id, repeated_revision).await?; - // Indexing the block's rows - build_context.block_meta_data.rows.iter().for_each(|row| { - let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id); - }); + for block_meta_data in build_context.blocks_meta_data { + let block_id = block_meta_data.block_id.clone(); - // Create grid's block - let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data); - let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes(); - let repeated_revision: RepeatedRevision = - Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into(); - let _ = grid_manager - .create_grid_block_meta(&block_id, repeated_revision) - .await?; + // Indexing the block's rows + block_meta_data.rows.iter().for_each(|row| { + let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id); + }); + + // Create grid's block + let grid_block_meta_delta = make_block_meta_delta(&block_meta_data); + let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes(); + let repeated_revision: RepeatedRevision = + Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into(); + let _ = grid_manager + .create_grid_block_meta(&block_id, repeated_revision) + .await?; + } Ok(grid_delta_data) } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs index d1cb847b37..d37c869156 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs @@ -1,6 +1,6 @@ use bytes::Bytes; use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::entities::{CellMeta, RowMeta, RowMetaChangeset, RowOrder}; +use flowy_grid_data_model::entities::{CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset, RowOrder}; use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockMetaPad}; use flowy_sync::entities::revision::Revision; @@ -41,6 +41,10 @@ impl GridBlockMetaEditor { }) } + pub async fn duplicate_block_meta_data(&self, duplicated_block_id: &str) -> GridBlockMetaData { + self.pad.read().await.duplicate_data(duplicated_block_id).await + } + /// return current number of rows and the inserted index. The inserted index will be None if the start_row_id is None pub(crate) async fn create_row( &self, diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs index 6ce36488c2..ec7c47f069 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs @@ -47,7 +47,7 @@ impl GridBlockManager { debug_assert!(!block_id.is_empty()); match self.block_editor_map.get(block_id) { None => { - tracing::error!("The is a fatal error, block is not exist"); + tracing::error!("This is a fatal error, block with id:{} is not exist", block_id); let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?); self.block_editor_map.insert(block_id.to_owned(), editor.clone()); Ok(editor) @@ -267,6 +267,7 @@ async fn make_block_meta_editor_map( } async fn make_block_meta_editor(user: &Arc, block_id: &str) -> FlowyResult { + tracing::trace!("Open block:{} meta editor", block_id); let token = user.token()?; let user_id = user.user_id()?; let pool = user.db_pool()?; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs index 82970ab25a..9c830797ed 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs @@ -77,7 +77,7 @@ impl NumberTypeOption { } fn cell_content_from_number_str(&self, s: &str) -> FlowyResult { - return match self.format { + match self.format { NumberFormat::Number => { if let Ok(v) = s.parse::() { return Ok(v.to_string()); @@ -94,7 +94,7 @@ impl NumberTypeOption { Ok(content) } _ => self.money_from_number_str(s), - }; + } } pub fn set_format(&mut self, format: NumberFormat) { @@ -173,7 +173,9 @@ impl CellDataOperation for NumberTypeOption { Ok(DecodedCellData::new(content)) } _ => { - let content = self.money_from_number_str(&cell_data).unwrap_or("".to_string()); + let content = self + .money_from_number_str(&cell_data) + .unwrap_or_else(|_| "".to_string()); Ok(DecodedCellData::new(content)) } } 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 e4cee15a6e..82db6aac1b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -487,6 +487,35 @@ impl GridMetaEditor { self.grid_pad.read().await.delta_bytes() } + pub async fn duplicate_grid(&self) -> FlowyResult { + let grid_pad = self.grid_pad.read().await; + let original_blocks = grid_pad.get_block_metas(); + let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_meta().await; + + let mut blocks_meta_data = vec![]; + if original_blocks.len() == duplicated_blocks.len() { + for (index, original_block_meta) in original_blocks.iter().enumerate() { + let grid_block_meta_editor = self.block_manager.get_editor(&original_block_meta.block_id).await?; + let duplicated_block_id = &duplicated_blocks[index].block_id; + + tracing::trace!("Duplicate block:{} meta data", duplicated_block_id); + let duplicated_block_meta_data = grid_block_meta_editor + .duplicate_block_meta_data(duplicated_block_id) + .await; + blocks_meta_data.push(duplicated_block_meta_data); + } + } else { + debug_assert_eq!(original_blocks.len(), duplicated_blocks.len()); + } + drop(grid_pad); + + Ok(BuildGridContext { + field_metas: duplicated_fields, + blocks: duplicated_blocks, + blocks_meta_data, + }) + } + async fn modify(&self, f: F) -> FlowyResult<()> where F: for<'a> FnOnce(&'a mut GridMetaPad) -> FlowyResult>, diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 41835804af..846dddd55e 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -173,7 +173,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor { }) } - fn delta_bytes(&self, view_id: &str) -> FutureResult { + fn view_delta_data(&self, view_id: &str) -> FutureResult { let view_id = view_id.to_string(); let manager = self.0.clone(); FutureResult::new(async move { @@ -197,7 +197,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor { }) } - fn process_create_view_data( + fn process_view_delta_data( &self, _user_id: &str, _view_id: &str, @@ -245,13 +245,13 @@ impl ViewDataProcessor for GridViewDataProcessor { }) } - fn delta_bytes(&self, view_id: &str) -> FutureResult { + fn view_delta_data(&self, view_id: &str) -> FutureResult { let view_id = view_id.to_string(); let grid_manager = self.0.clone(); FutureResult::new(async move { let editor = grid_manager.open_grid(view_id).await?; - let delta_bytes = editor.delta_bytes().await; - Ok(delta_bytes) + let delta_bytes = editor.duplicate_grid().await?; + Ok(delta_bytes.into()) }) } @@ -264,7 +264,7 @@ impl ViewDataProcessor for GridViewDataProcessor { FutureResult::new(async move { make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await }) } - fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec) -> FutureResult { + fn process_view_delta_data(&self, user_id: &str, view_id: &str, data: Vec) -> FutureResult { let user_id = user_id.to_string(); let view_id = view_id.to_string(); let grid_manager = self.0.clone(); diff --git a/shared-lib/flowy-grid-data-model/src/entities/meta.rs b/shared-lib/flowy-grid-data-model/src/entities/meta.rs index 328b368fbc..d62965245b 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/meta.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/meta.rs @@ -203,11 +203,17 @@ impl CellMeta { } } -#[derive(Clone, Deserialize, Serialize)] +#[derive(Clone, Default, Deserialize, Serialize)] pub struct BuildGridContext { pub field_metas: Vec, - pub block_meta: GridBlockMeta, - pub block_meta_data: GridBlockMetaData, + pub blocks: Vec, + pub blocks_meta_data: Vec, +} + +impl BuildGridContext { + pub fn new() -> Self { + Self::default() + } } impl std::convert::From for Bytes { @@ -225,19 +231,3 @@ impl std::convert::TryFrom for BuildGridContext { Ok(ctx) } } - -impl std::default::Default for BuildGridContext { - fn default() -> Self { - let block_meta = GridBlockMeta::new(); - let block_meta_data = GridBlockMetaData { - block_id: block_meta.block_id.clone(), - rows: vec![], - }; - - Self { - field_metas: vec![], - block_meta, - block_meta_data, - } - } -} diff --git a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs index 3370719e68..175890ad01 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs @@ -1,7 +1,9 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_delta_from_revisions}; -use flowy_grid_data_model::entities::{gen_block_id, CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset}; +use flowy_grid_data_model::entities::{ + gen_block_id, gen_row_id, CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset, +}; use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -22,6 +24,23 @@ pub struct GridBlockMetaPad { } impl GridBlockMetaPad { + pub async fn duplicate_data(&self, duplicated_block_id: &str) -> GridBlockMetaData { + let duplicated_rows = self + .rows + .iter() + .map(|row| { + let mut duplicated_row = row.as_ref().clone(); + duplicated_row.id = gen_row_id(); + duplicated_row.block_id = duplicated_block_id.to_string(); + duplicated_row + }) + .collect::>(); + GridBlockMetaData { + block_id: duplicated_block_id.to_string(), + rows: duplicated_rows, + } + } + pub fn from_delta(delta: GridBlockMetaDelta) -> CollaborateResult { let s = delta.to_str()?; let meta_data: GridBlockMetaData = serde_json::from_str(&s).map_err(|e| { diff --git a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs index fb800a9626..95e707a287 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_builder.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_builder.rs @@ -1,11 +1,27 @@ use crate::errors::{CollaborateError, CollaborateResult}; -use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, RowMeta}; +use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, GridBlockMeta, GridBlockMetaData, RowMeta}; -#[derive(Default)] pub struct GridBuilder { build_context: BuildGridContext, } +impl std::default::Default for GridBuilder { + fn default() -> Self { + let mut build_context = BuildGridContext::new(); + + let block_meta = GridBlockMeta::new(); + let block_meta_data = GridBlockMetaData { + block_id: block_meta.block_id.clone(), + rows: vec![], + }; + + build_context.blocks.push(block_meta); + build_context.blocks_meta_data.push(block_meta_data); + + GridBuilder { build_context } + } +} + impl GridBuilder { pub fn add_field(mut self, field: FieldMeta) -> Self { self.build_context.field_metas.push(field); @@ -13,9 +29,11 @@ impl GridBuilder { } pub fn add_empty_row(mut self) -> Self { - let row = RowMeta::new(&self.build_context.block_meta.block_id); - self.build_context.block_meta_data.rows.push(row); - self.build_context.block_meta.row_count += 1; + let row = RowMeta::new(&self.build_context.blocks.first().unwrap().block_id); + let block_meta = self.build_context.blocks.first_mut().unwrap(); + let block_meta_data = self.build_context.blocks_meta_data.first_mut().unwrap(); + block_meta_data.rows.push(row); + block_meta.row_count += 1; self } @@ -57,13 +75,13 @@ mod tests { let grid_meta = GridMeta { grid_id, fields: build_context.field_metas, - blocks: vec![build_context.block_meta], + blocks: build_context.blocks, }; let grid_meta_delta = make_grid_delta(&grid_meta); let _: GridMeta = serde_json::from_str(&grid_meta_delta.to_str().unwrap()).unwrap(); - let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data); + let grid_block_meta_delta = make_block_meta_delta(build_context.blocks_meta_data.first().unwrap()); let _: GridBlockMetaData = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap(); } } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs index 791648cf14..0e255f0f12 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs @@ -3,8 +3,8 @@ use crate::errors::{internal_error, CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_delta_from_revisions}; use bytes::Bytes; use flowy_grid_data_model::entities::{ - gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, GridBlockMetaChangeset, - GridMeta, + gen_block_id, gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, + GridBlockMetaChangeset, GridMeta, }; use lib_infra::util::move_vec_element; use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; @@ -24,6 +24,28 @@ pub trait JsonDeserializer { } impl GridMetaPad { + pub async fn duplicate_grid_meta(&self) -> (Vec, Vec) { + let fields = self + .grid_meta + .fields + .iter() + .map(|field| field.clone()) + .collect::>(); + + let blocks = self + .grid_meta + .blocks + .iter() + .map(|block| { + let mut duplicated_block = block.clone(); + duplicated_block.block_id = gen_block_id(); + duplicated_block + }) + .collect::>(); + + (fields, blocks) + } + pub fn from_delta(delta: GridMetaDelta) -> CollaborateResult { let s = delta.to_str()?; let grid: GridMeta = serde_json::from_str(&s) From 03879a7262364d699def025130ccd37a9b81b543 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 6 Jun 2022 21:22:07 +0800 Subject: [PATCH 76/82] chore: udpate change log --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ac013c35..5ee49d36f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Release Notes +## Version 0.0.4 - 2022-06-06 +- Drag to adjust the width of a column +- Upgrade to Flutter 3.0 +- Native support for M1 chip +- Date supports time formats +- New property: URL +- Keyboard shortcuts support for Grid: press Enter to leave the edit mode; control c/v to copy-paste cell values + +### Bug Fixes +- Fixed some bugs + + +## Version 0.0.4 - beta.3 - 2022-05-02 +- Drag to reorder app/ view/ field +- Row record open as a page +- Auto resize the height of the row in the grid +- Support more number formats +- Search column options, supporting Single select, Multi-select, and number format + +![May-03-2022 10-03-00](https://user-images.githubusercontent.com/86001920/166394640-a8f1f3bc-5f20-4033-93e9-16bc308d7005.gif) + + +### Bug Fixes & Improvements +- Improved row/cell data cache +- Fixed some bugs + + +## Version 0.0.4 - beta.2 - 2022-04-11 + + - Support properties: Text, Number, Date, Checkbox, Select, Multi-select + - Insert / delete rows + - Add / delete / hide columns + - Edit property + ![](https://user-images.githubusercontent.com/12026239/162753644-bf2f4e7a-2367-4d48-87e6-35e244e83a5b.png) + ## Version 0.0.4 - beta.1 - 2022-04-08 v0.0.4 - beta.1 is pre-release From 786cfc51643eed67e777eb9b53027d88e737b72d Mon Sep 17 00:00:00 2001 From: tsuiyuenhong Date: Wed, 8 Jun 2022 00:54:21 +0800 Subject: [PATCH 77/82] feat: only build the specify archs in macOS platform --- frontend/Makefile.toml | 3 +++ frontend/app_flowy/macos/Podfile | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 0dbc58d572..42708d7c32 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -68,6 +68,7 @@ RUST_COMPILE_TARGET = "aarch64-apple-darwin" FLUTTER_OUTPUT_DIR = "Release" PRODUCT_EXT = "app" APP_ENVIRONMENT = "production" +BUILD_ARCHS = "arm64" [env.production-mac-x86_64] BUILD_FLAG = "release" @@ -76,6 +77,7 @@ RUST_COMPILE_TARGET = "x86_64-apple-darwin" FLUTTER_OUTPUT_DIR = "Release" PRODUCT_EXT = "app" APP_ENVIRONMENT = "production" +BUILD_ARCHS = "x86_64" [env.development-windows-x86] TARGET_OS = "windows" @@ -146,6 +148,7 @@ script = [ echo PRODUCT_EXT: ${PRODUCT_EXT} echo APP_ENVIRONMENT: ${APP_ENVIRONMENT} echo ${platforms} + echo ${BUILD_ARCHS} ''' ] script_runner = "@shell" diff --git a/frontend/app_flowy/macos/Podfile b/frontend/app_flowy/macos/Podfile index dade8dfad0..e806f574bd 100644 --- a/frontend/app_flowy/macos/Podfile +++ b/frontend/app_flowy/macos/Podfile @@ -26,6 +26,23 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_macos_podfile_setup +def build_specify_archs_only + if ENV.has_key?('BUILD_ARCHS') + xcodeproj_path = File.dirname(__FILE__) + '/Runner.xcodeproj' + project = Xcodeproj::Project.open(xcodeproj_path) + project.targets.each do |target| + if target.name == 'Runner' + target.build_configurations.each do |config| + config.build_settings['ARCHS'] = ENV['BUILD_ARCHS'] + end + end + end + project.save() + end +end + +build_specify_archs_only() + target 'Runner' do use_frameworks! use_modular_headers! From 3c25bc871ed7ac827863ff09d3e4d2b1826127e3 Mon Sep 17 00:00:00 2001 From: Daniil Golubev Date: Tue, 7 Jun 2022 20:49:41 +0000 Subject: [PATCH 78/82] feat: update russian translations --- .../app_flowy/assets/translations/ru-RU.json | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/frontend/app_flowy/assets/translations/ru-RU.json b/frontend/app_flowy/assets/translations/ru-RU.json index d729c0a737..65e61347e6 100644 --- a/frontend/app_flowy/assets/translations/ru-RU.json +++ b/frontend/app_flowy/assets/translations/ru-RU.json @@ -141,6 +141,68 @@ "lightLabel": "Светлая тема", "darkLabel": "Тёмная тема" } + }, + "grid": { + "settings": { + "filter": "Фильтр", + "sortBy": "Сортировать", + "Properties": "Свойства" + }, + "field": { + "hide": "Скрыть", + "insertLeft": "Вставить слева", + "insertRight": "Вставить справа", + "duplicate": "Дублировать", + "delete": "Удалить", + "textFieldName": "Текст", + "checkboxFieldName": "Checkbox", + "dateFieldName": "Дата", + "numberFieldName": "Число", + "singleSelectFieldName": "Выбор", + "multiSelectFieldName": "Выбор многих", + "urlFieldName": "URL", + "numberFormat": " Формат числа", + "dateFormat": " Формат даты", + "includeTime": " Время", + "dateFormatFriendly": "День Месяц, Год", + "dateFormatISO": "Год-Месяц-День", + "dateFormatLocal": "Год/Месяц/День", + "dateFormatUS": "Год/Месяц/День", + "timeFormat": " Форматировать время", + "invalidTimeFormat": "Неверный формат", + "timeFormatTwelveHour": "12 часов", + "timeFormatTwentyFourHour": "24 часа", + "addSelectOption": "Добавить вариант", + "optionTitle": "Варианты", + "addOption": "Добавить", + "editProperty": "Редактировать свойство" + }, + "row": { + "duplicate": "Дублировать", + "delete": "Удалить", + "textPlaceholder": "Пусто", + "copyProperty": "Свойство скопировано" + }, + "selectOption": { + "create": "Создать", + "purpleColor": "Фиолетовый", + "pinkColor": "Розовый", + "lightPinkColor": "Светло-розовый", + "orangeColor": "Оранжевый", + "yellowColor": "Желтый", + "limeColor": "Ярко-зелёный", + "greenColor": "Зелёный", + "aquaColor": "Морской волны", + "blueColor": "Синий", + "deleteTag": "Удалить вариант", + "colorPannelTitle": "Цвета", + "pannelTitle": "Выберите или создайте вариант", + "searchOption": "Поиск" + }, + "date": { + "timeHintTextInTwelveHour": "12:00 AM", + "timeHintTextInTwentyFourHour": "12:00" + } } } \ No newline at end of file From 1bc30ee1dcee5fd45a788998cd6db0a885fe5b6d Mon Sep 17 00:00:00 2001 From: RaphGL Date: Wed, 8 Jun 2022 18:20:53 +0100 Subject: [PATCH 79/82] fix: brazilian portuguese translation mistakes --- .../app_flowy/assets/translations/pt-BR.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frontend/app_flowy/assets/translations/pt-BR.json b/frontend/app_flowy/assets/translations/pt-BR.json index e2f0b27032..8ae5818b60 100644 --- a/frontend/app_flowy/assets/translations/pt-BR.json +++ b/frontend/app_flowy/assets/translations/pt-BR.json @@ -7,11 +7,11 @@ "letsGoButtonText": "Vamos lá", "title": "Título", "signUp": { - "buttonText": "Inscreve-se", - "title": "Inscrever-se @:appName", + "buttonText": "Se inscreva", + "title": "Se inscreva no @:appName", "getStartedText": "Começar", - "emptyPasswordError": "Senha não pode ser em branco.", - "repeatPasswordEmptyError": "Confirmar a senha não pode ser em branco.", + "emptyPasswordError": "Senha não pode estar em branco.", + "repeatPasswordEmptyError": "Confirmar a senha não pode estar em branco.", "unmatchedPasswordError": "As senhas não conferem.", "alreadyHaveAnAccount": "Já possui uma conta?", "emailHint": "Email", @@ -19,14 +19,14 @@ "repeatPasswordHint": "Confirme a senha" }, "signIn": { - "loginTitle": "Login to @:appName", + "loginTitle": "Entre no @:appName", "loginButtonText": "Login", "buttonText": "Entre", "forgotPassword": "Esqueceu a senha?", "emailHint": "Email", "passwordHint": "Senha", "dontHaveAnAccount": "Não possui uma conta?", - "repeatPasswordEmptyError": "Confirmar a senha não pode ser em branco.", + "repeatPasswordEmptyError": "Confirmar a senha não pode estar em branco.", "unmatchedPasswordError": "As senhas não conferem." }, "workspace": { @@ -67,7 +67,7 @@ "whatsNew": "O que há de novo?", "help": "Ajuda & Suporte", "debug": { - "name": "Informação de debug", + "name": "Informação de depuração", "success": "Copiar informação de debug para o clipboard!", "fail": "Falha em copiar a informação de debug para o clipboard" } @@ -104,7 +104,7 @@ }, "button": { "OK": "OK", - "Cancel": "Canelar", + "Cancel": "Cancelar", "signIn": "Entrar", "signOut": "Sair", "complete": "Completar", @@ -143,4 +143,5 @@ } } } - \ No newline at end of file + + From 86ebb314e8b69f0b1605f15bb42e298e923dbb65 Mon Sep 17 00:00:00 2001 From: RaphGL Date: Wed, 8 Jun 2022 18:24:08 +0100 Subject: [PATCH 80/82] feat: added european portuguese translation --- .../app_flowy/assets/translations/pt-PT.json | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 frontend/app_flowy/assets/translations/pt-PT.json diff --git a/frontend/app_flowy/assets/translations/pt-PT.json b/frontend/app_flowy/assets/translations/pt-PT.json new file mode 100644 index 0000000000..3aa37ee230 --- /dev/null +++ b/frontend/app_flowy/assets/translations/pt-PT.json @@ -0,0 +1,146 @@ +{ + "appName": "AppFlowy", + "defaultUsername": "Me", + "welcomeText": "Bem vindo ao @:appName", + "githubStarText": "Star on GitHub", + "subscribeNewsletterText": "Inscreve-te ao Newsletter", + "letsGoButtonText": "Bora", + "title": "Título", + "signUp": { + "buttonText": "Inscreve-te", + "title": "Inscreve-te ao @:appName", + "getStartedText": "Começar", + "emptyPasswordError": "A palavra-passe não pode estar em branco.", + "repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.", + "unmatchedPasswordError": "As palavras-passes não coincidem.", + "alreadyHaveAnAccount": "Já possuis uma conta?", + "emailHint": "Email", + "passwordHint": "Password", + "repeatPasswordHint": "Confirma a tua password" + }, + "signIn": { + "loginTitle": "Entre no @:appName", + "loginButtonText": "Login", + "buttonText": "Entre", + "forgotPassword": "Esqueceste-te da tua palavra-passe?", + "emailHint": "Email", + "passwordHint": "Palavra-passe", + "dontHaveAnAccount": "Não possuis uma conta?", + "repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.", + "unmatchedPasswordError": "As palavras-passes não conferem." + }, + "workspace": { + "create": "Cria um ambiente de trabalho", + "hint": "ambiente de trabalho", + "notFoundError": "Ambiente de trabalho não encontrada" + }, + "shareAction": { + "buttonText": "Partilhar", + "workInProgress": "Em breve", + "markdown": "Markdown", + "copyLink": "Copiar o link" + }, + "disclosureAction": { + "rename": "Renomear", + "delete": "Apagar", + "duplicate": "Duplicar" + }, + "blankPageTitle": "Página em branco", + "newPageText": "Nova página", + "trash": { + "text": "Lixo", + "restoreAll": "Restaurar todos", + "deleteAll": "Apagar todos", + "pageHeader": { + "fileName": "Nome do ficheiro", + "lastModified": "Última modificação", + "created": "Criado" + } + }, + "deletePagePrompt": { + "text": "Esta página está no lixo", + "restore": "Restaurar a página", + "deletePermanent": "Apagar permanentemente" + }, + "dialogCreatePageNameHint": "Nome da página", + "questionBubble": { + "whatsNew": "O que há de novo?", + "help": "Ajuda & Suporte", + "debug": { + "name": "Informação de depuração", + "success": "Copiar informação de depuração para o clipboard!", + "fail": "Falha em copiar a informação de depuração para o clipboard" + } + }, + "menuAppHeader": { + "addPageTooltip": "Adiciona uma nova página.", + "defaultNewPageName": "Sem título", + "renameDialog": "Renomear" + }, + "toolbar": { + "undo": "Desfazer", + "redo": "Refazer", + "bold": "Negrito", + "italic": "Itálico", + "underline": "Sublinhado", + "strike": "Riscado", + "numList": "Lista numerada", + "bulletList": "Lista com marcadores", + "checkList": "Lista de verificação", + "inlineCode": "Embutir código", + "quote": "Citação em bloco", + "header": "Cabeçalho", + "highlight": "Realçar" + }, + "tooltip": { + "lightMode": "Mudar para o modo Claro.", + "darkMode": "Mudar para o modo Escuro." + }, + "contactsPage": { + "title": "Conctatos", + "whatsHappening": "O que está a acontecer nesta semana?", + "addContact": "Adicionar um conctato", + "editContact": "Editar um conctato" + }, + "button": { + "OK": "OK", + "Cancel": "Cancelar", + "signIn": "Entrar", + "signOut": "Sair", + "complete": "Completar", + "save": "Guardar" + }, + "label": { + "welcome": "Bem vindo!", + "firstName": "Nome", + "middleName": "Nome do Meio", + "lastName": "Apelido", + "stepX": "Passo {X}" + }, + "oAuth": { + "err": { + "failedTitle": "Erro ao conectar à sua conta.", + "failedMsg": "Verifica se concluiste o processo de login no teu navegador." + }, + "google": { + "title": "GOOGLE SIGN-IN", + "instruction1": "Para importar os teus Conctatos do Google, tens de autorizar esta aplicação usando o teu navegador web.", + "instruction2": "Copia este código para a tua área de transferências clicando no ícone ou selecionando o texto:", + "instruction3": "Navega até o link a seguir no seu navegador e digite o código acima:", + "instruction4": "Clica no botão abaixo ao concluir a inscrição:" + } + }, + "settings": { + "title": "Definições", + "menu": { + "appearance": "Aparência", + "language": "Idioma", + "open": "Abrir as Definições" + }, + "appearance": { + "lightLabel": "Modo Claro", + "darkLabel": "Modo Escuro" + } + } + } + From 582733386cbb49601050b30b5a35f026d226c800 Mon Sep 17 00:00:00 2001 From: Sean Riley Hawkins Date: Thu, 9 Jun 2022 10:25:38 +0200 Subject: [PATCH 81/82] refactor: used simple toast than styled dialog --- .../presentation/plugins/doc/document.dart | 6 +-- .../presentation/widgets/dialogs.dart | 39 ------------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart index 7c2a47a8c1..f0609df31d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/doc/document.dart @@ -11,6 +11,7 @@ import 'package:app_flowy/workspace/application/appearance.dart'; import 'package:app_flowy/workspace/application/doc/share_bloc.dart'; import 'package:app_flowy/workspace/application/view/view_listener.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart'; import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart'; import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; @@ -179,10 +180,7 @@ class DocumentShareButton extends StatelessWidget { switch (action) { case ShareAction.markdown: context.read().add(const DocShareEvent.shareMarkdown()); - BubbleNotification( - msgTitle: LocaleKeys.notifications_export_markdown.tr(), - msgBody: 'Path: ${LocaleKeys.notifications_export_path.tr()}') - .show(context); + showMessageToast('Exported to: ${LocaleKeys.notifications_export_path.tr()}'); break; case ShareAction.copyLink: FlowyAlertDialog(title: LocaleKeys.shareAction_workInProgress.tr()).show(context); diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index c17b386df0..3a5c1c79e5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -219,42 +219,3 @@ class OkCancelButton extends StatelessWidget { ); } } - -class BubbleNotification extends StatefulWidget { - final String msgTitle; - final String msgBody; - - const BubbleNotification({Key? key, required this.msgTitle, required this.msgBody}) : super(key: key); - - @override - State createState() => _BubbleNotification(); -} - -class _BubbleNotification extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return StyledDialog( - // maxWidth: 800, - maxHeight: 200, - shrinkWrap: true, - child: Card( - margin: const EdgeInsets.symmetric(horizontal: 4), - child: SafeArea( - child: ListTile( - leading: SizedBox.fromSize( - size: const Size(40, 40), - child: const ClipOval( - child: Icon(Icons.file_copy), - ), - ), - title: Text(widget.msgTitle), - subtitle: Text(widget.msgBody), - ), - ))); - } -} From c93b2a8635e70a4bc0105b69358bbb2ed8fbf194 Mon Sep 17 00:00:00 2001 From: MikeWallaceDev Date: Thu, 9 Jun 2022 21:38:20 -0400 Subject: [PATCH 82/82] refactor: simplify launch settings delete duplicate .vscode folder in app_flowy rename launches and tasks --- frontend/.vscode/launch.json | 48 ++++++--- frontend/.vscode/tasks.json | 104 +++++++++++++++--- frontend/app_flowy/.vscode/launch.json | 48 --------- frontend/app_flowy/.vscode/settings.json | 26 ----- frontend/app_flowy/.vscode/tasks.json | 129 ----------------------- 5 files changed, 123 insertions(+), 232 deletions(-) delete mode 100644 frontend/app_flowy/.vscode/launch.json delete mode 100644 frontend/app_flowy/.vscode/settings.json delete mode 100644 frontend/app_flowy/.vscode/tasks.json diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index 66fc02b1a1..4328389d39 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -5,40 +5,60 @@ "version": "0.2.0", "configurations": [ { - "name": "app_flowy", + // This task builds the Rust and Dart code of AppFlowy. + "name": "AF: Build All", "request": "launch", "program": "./lib/main.dart", "type": "dart", - "preLaunchTask": "build_flowy_sdk", + "preLaunchTask": "AF: build_flowy_sdk", "env":{ "RUST_LOG":"info" }, "cwd": "${workspaceRoot}/app_flowy" }, { - "name": "app_flowy(trace)", + // This task only builds the Dart code of AppFlowy. + "name": "AF: Build Dart Only", + "request": "launch", + "program": "${workspaceRoot}/lib/main.dart", + "type": "dart", + "env": { + "RUST_LOG": "debug" + }, + "cwd": "${workspaceRoot}" + }, + { + // This task builds will: + // - call the clean task, + // - rebuild all the generated Files (including freeze and language files) + // - rebuild the the Rust and Dart code of AppFlowy. + "name": "AF: Clean + Rebuild All", "request": "launch", "program": "./lib/main.dart", "type": "dart", - "preLaunchTask": "build_flowy_sdk", + "preLaunchTask": "AF: Clean + Rebuild All", + "env":{ + "RUST_LOG":"info" + }, + "cwd": "${workspaceRoot}/app_flowy" + }, + + { + "name": "AF: Build All (rustlog: trace)", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: build_flowy_sdk", "env":{ "RUST_LOG":"trace" }, "cwd": "${workspaceRoot}/app_flowy" }, { - "name": "app_flowy (profile mode)", + "name": "AF: app_flowy (profile mode)", "request": "launch", "type": "dart", "flutterMode": "profile" }, - { - "name": "Generate Language Files", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "Generate Language Files", - "cwd": "${workspaceRoot}/app_flowy/" - }, ] -} \ No newline at end of file +} diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json index e902e3dfd5..aec5955c19 100644 --- a/frontend/.vscode/tasks.json +++ b/frontend/.vscode/tasks.json @@ -10,13 +10,35 @@ // ${cwd}: the current working directory of the spawned process "tasks": [ { - "label": "build_flowy_sdk", + "label": "AF: Clean + Rebuild All", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "AF: Clean", + "AF: Flutter Pub", + "AF: Flutter Package Get", + "AF: Generate Language Files", + "AF: Generate Freezed Files", + "AF: build_flowy_sdk" + ], + "group": { + "kind": "build", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + } + + }, + { + "label": "AF: build_flowy_sdk", "type": "shell", "command": "sh ./scripts/build_sdk.sh", "windows": { "options": { "env": { - "FLOWY_DEV_ENV": "Windows", + "FLOWY_DEV_ENV": "Windows" }, "shell": { "executable": "cmd.exe", @@ -31,27 +53,67 @@ "linux": { "options": { "env": { - "FLOWY_DEV_ENV": "Linux-x86", + "FLOWY_DEV_ENV": "Linux-x86" } - }, + } }, "osx": { "options": { "env": { - "FLOWY_DEV_ENV": "macOS", + "FLOWY_DEV_ENV": "macOS" } - }, + } }, "group": "build", "options": { "cwd": "${workspaceFolder}" - }, - // "problemMatcher": [ - // "$rustc" - // ], + } }, { - "label": "Generate Language Files", + "label": "AF: Code Gen", + "type": "shell", + "dependsOrder": "sequence", + "dependsOn": [ + "AF: Flutter Pub", + "AF: Flutter Package Get", + "AF: Generate Language Files", + "AF: Generate Freezed Files" + ], + "group": { + "kind": "build", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + } + }, + { + "label": "AF: Flutter Pub", + "type": "shell", + "command": "flutter pub get", + "options": { + "cwd": "${workspaceFolder}/app_flowy" + } + }, + { + "label": "AF: Flutter Package Get", + "type": "shell", + "command": "flutter packages pub get", + "options": { + "cwd": "${workspaceFolder}/app_flowy" + } + }, + { + "label": "AF: Generate Freezed Files", + "type": "shell", + "command": "flutter pub run build_runner build --delete-conflicting-outputs", + "options": { + "cwd": "${workspaceFolder}/app_flowy" + } + }, + { + "label": "AF: Generate Language Files", "type": "shell", "command": "sh ./scripts/generate_language_files.sh", "windows": { @@ -69,10 +131,10 @@ "group": "build", "options": { "cwd": "${workspaceFolder}" - }, + } }, { - "label": "Clean", + "label": "AF: Clean", "type": "shell", "command": "sh ./scripts/clean.sh", "windows": { @@ -90,7 +152,19 @@ "group": "build", "options": { "cwd": "${workspaceFolder}" - }, + } + }, + { + "label": "AF: flutter build aar", + "type": "flutter", + "command": "flutter", + "args": [ + "build", + "aar" + ], + "group": "build", + "problemMatcher": [], + "detail": "app_flowy" } ] -} \ No newline at end of file +} diff --git a/frontend/app_flowy/.vscode/launch.json b/frontend/app_flowy/.vscode/launch.json deleted file mode 100644 index e5ea3cdf6f..0000000000 --- a/frontend/app_flowy/.vscode/launch.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - // This task builds the Rust and Dart code of AppFlowy. - "name": "Build", - "request": "launch", - "program": "${workspaceRoot}/lib/main.dart", - "preLaunchTask": "build_flowy_sdk", - "type": "dart", - "env": { - "RUST_LOG": "debug" - }, - "cwd": "${workspaceRoot}" - }, - { - // This task only build the Dart code of AppFlowy. - "name": "Build (Dart)", - "request": "launch", - "program": "${workspaceRoot}/lib/main.dart", - "type": "dart", - "env": { - "RUST_LOG": "debug" - }, - "cwd": "${workspaceRoot}" - }, - { - "name": "Build (trace log)", - "request": "launch", - "program": "${workspaceRoot}/lib/main.dart", - "type": "dart", - "preLaunchTask": "build_flowy_sdk", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}" - }, - { - "name": "Build (profile mode)", - "request": "launch", - "type": "dart", - "flutterMode": "profile" - }, - ] -} \ No newline at end of file diff --git a/frontend/app_flowy/.vscode/settings.json b/frontend/app_flowy/.vscode/settings.json deleted file mode 100644 index 13845cc225..0000000000 --- a/frontend/app_flowy/.vscode/settings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "[dart]": { - "editor.formatOnSave": true, - "editor.formatOnType": true, - "editor.rulers": [ - 120 - ], - "editor.selectionHighlight": false, - "editor.suggest.snippetsPreventQuickSuggestions": false, - "editor.suggestSelection": "first", - "editor.tabCompletion": "onlySnippets", - "editor.wordBasedSuggestions": false - }, - "svgviewer.enableautopreview": true, - "svgviewer.previewcolumn": "Active", - "svgviewer.showzoominout": true, - "editor.wordWrapColumn": 120, - "editor.minimap.maxColumn": 140, - "prettier.printWidth": 140, - "editor.wordWrap": "wordWrapColumn", - "dart.lineLength": 120, - "files.associations": { - "*.log.*": "log" - }, - "editor.formatOnSave": true, -} \ No newline at end of file diff --git a/frontend/app_flowy/.vscode/tasks.json b/frontend/app_flowy/.vscode/tasks.json deleted file mode 100644 index f46aac1085..0000000000 --- a/frontend/app_flowy/.vscode/tasks.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "version": "2.0.0", - // https://code.visualstudio.com/docs/editor/tasks - // https://gist.github.com/deadalusai/9e13e36d61ec7fb72148 - // ${workspaceRoot}: the root folder of the team - // ${file}: the current opened file - // ${fileBasename}: the current opened file's basename - // ${fileDirname}: the current opened file's dirname - // ${fileExtname}: the current opened file's extension - // ${cwd}: the current working directory of the spawned process - "tasks": [ - { - "label": "build_flowy_sdk", - "type": "shell", - "command": "sh ./scripts/build_sdk.sh", - "windows": { - "options": { - "env": { - "FLOWY_DEV_ENV": "Windows", - }, - "shell": { - "executable": "cmd.exe", - "args": [ - "/d", - "/c", - ".\\scripts\\build_sdk.cmd" - ] - } - } - }, - "linux": { - "options": { - "env": { - "FLOWY_DEV_ENV": "Linux-x86", - } - }, - }, - "osx": { - "options": { - "env": { - "FLOWY_DEV_ENV": "macOS", - } - }, - }, - "group": "build", - "options": { - "cwd": "${workspaceFolder}/../" - }, - // "problemMatcher": [ - // "$rustc" - // ], - }, - { - "label": "Code Gen", - "type": "shell", - "dependsOn": [ - "Flutter Pub", - "Flutter Package Get", - "Generate Language Files", - "Generate Freezed Files" - ], - "group": { - "kind": "build", - "isDefault": true, - }, - "dependsOrder": "sequence", - "presentation": { - "reveal": "always", - "panel": "new" - }, - }, - { - "label": "Flutter Pub", - "type": "shell", - "command": "flutter pub get", - }, - { - "label": "Flutter Package Get", - "type": "shell", - "command": "flutter packages pub get", - }, - { - "label": "Generate Freezed Files", - "type": "shell", - "command": "flutter pub run build_runner build --delete-conflicting-outputs", - }, - { - "label": "Generate Language Files", - "type": "shell", - "command": "sh ./scripts/generate_language_files.sh", - "windows": { - "options": { - "shell": { - "executable": "cmd.exe", - "args": [ - "/d", - "/c", - ".\\scripts\\generate_language_files.cmd" - ] - } - } - }, - "group": "build", - "options": { - "cwd": "${workspaceFolder}/../" - }, - }, - { - "label": "Clean", - "type": "shell", - "command": "sh ./scripts/clean.sh", - "windows": { - "options": { - "shell": { - "executable": "cmd.exe", - "args": [ - "/d", - "/c", - ".\\scripts\\clean.cmd" - ] - } - } - }, - "options": { - "cwd": "${workspaceFolder}/../" - }, - } - ] -} \ No newline at end of file