From 7d551534759e076c6f0387d65e44989ba7da2216 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 5 Dec 2023 17:34:42 +0800 Subject: [PATCH] feat: add quick edit panel (#4089) * feat: add quick edit panel * feat: improve datepicker color * fix: quick edit field editor overflow * chore: try to fix mobile ci * Revert "chore: try to fix mobile ci" This reverts commit 68f0ccecd69632784b783fa0b50ebd2dfe7d44fb. --- .../presentation/base/app_bar_actions.dart | 33 +++- .../presentation/base/mobile_view_page.dart | 4 +- .../show_mobile_bottom_sheet.dart | 14 +- .../card_detail/mobile_edit_field_screen.dart | 20 +-- .../widgets/_field_options_eidtor.dart | 42 ++--- .../widgets/option_text_field.dart | 35 +++++ .../card/card_detail/widgets/widgets.dart | 3 +- .../mobile_date_picker_screen.dart | 20 +-- .../field/bottom_sheet_create_field.dart | 30 +++- .../field/quick_edit_field_bottom_sheet.dart | 148 ++++++++++++++++++ .../setting/font/font_picker_screen.dart | 4 +- .../language/language_picker_screen.dart | 4 +- .../widgets/flowy_option_tile.dart | 8 +- .../base/color/color_picker_screen.dart | 4 +- .../plugins/base/icon/icon_picker_page.dart | 5 +- .../field/field_backend_service.dart | 69 ++++++++ .../widgets/header/mobile_field_cell.dart | 2 +- .../row/cells/date_cell/date_cell.dart | 2 + .../code_block/code_language_screen.dart | 4 +- .../image/flowy_image_picker.dart | 5 +- .../mobile_block_settings_screen.dart | 5 +- .../lib/startup/tasks/generate_router.dart | 1 - .../resources/flowy_icons/16x/insert_left.svg | 5 + .../flowy_icons/16x/insert_right.svg | 5 + 24 files changed, 371 insertions(+), 101 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart create mode 100644 frontend/appflowy_flutter/lib/mobile/presentation/database/field/quick_edit_field_bottom_sheet.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_backend_service.dart create mode 100644 frontend/resources/flowy_icons/16x/insert_left.svg create mode 100644 frontend/resources/flowy_icons/16x/insert_right.svg diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart index c4be98289b..a3af260d4b 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/base/app_bar_actions.dart @@ -2,24 +2,51 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; class AppBarBackButton extends StatelessWidget { const AppBarBackButton({ super.key, - required this.onTap, + this.onTap, }); - final VoidCallback onTap; + final VoidCallback? onTap; @override Widget build(BuildContext context) { return AppBarButton( - onTap: onTap, + onTap: onTap ?? () => context.pop(), child: const Icon(Icons.arrow_back_ios_new), ); } } +class AppBarCloseButton extends StatelessWidget { + const AppBarCloseButton({ + super.key, + this.onTap, + this.margin = const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 12.0, + ), + }); + + final VoidCallback? onTap; + final EdgeInsets margin; + + @override + Widget build(BuildContext context) { + return FlowyButton( + useIntrinsicWidth: true, + text: const Icon( + Icons.close, + ), + margin: margin, + onTap: onTap ?? () => context.pop(), + ); + } +} + class AppBarCancelButton extends StatelessWidget { const AppBarCancelButton({ super.key, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/mobile_view_page.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/mobile_view_page.dart index de7a2c5308..bef066ebb9 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/base/mobile_view_page.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/base/mobile_view_page.dart @@ -126,9 +126,7 @@ class _MobileViewPageState extends State { ), ], ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), actions: actions, ), body: SafeArea( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart index 55b8dff554..b91cc0da25 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/plugins/base/drag_handler.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder; @@ -15,6 +16,8 @@ Future showMobileBottomSheet( bool showCloseButton = false, String title = '', // only works if showHeader is true Color? backgroundColor, + bool isScrollControlled = true, + BoxConstraints? constraints, }) async { assert(() { if (showCloseButton || title.isNotEmpty) assert(showHeader); @@ -23,11 +26,12 @@ Future showMobileBottomSheet( return showModalBottomSheet( context: context, - isScrollControlled: true, + isScrollControlled: isScrollControlled, enableDrag: isDragEnabled, useSafeArea: true, clipBehavior: Clip.antiAlias, backgroundColor: backgroundColor, + constraints: constraints, shape: shape ?? const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( @@ -53,14 +57,8 @@ Future showMobileBottomSheet( showCloseButton ? Padding( padding: EdgeInsets.only(left: padding.left), - child: FlowyButton( - useIntrinsicWidth: true, - text: const Icon( - Icons.close, - size: 24, - ), + child: const AppBarCloseButton( margin: EdgeInsets.zero, - onTap: () => Navigator.of(context).pop(), ), ) : const SizedBox.shrink(), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart index 8f4c58616b..c35cea161d 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart @@ -1,8 +1,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart'; -import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; -import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart'; +import 'package:appflowy/plugins/database_view/application/field/field_backend_service.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -13,18 +12,15 @@ class MobileEditPropertyScreen extends StatefulWidget { static const routeName = '/edit_property'; static const argViewId = 'view_id'; static const argField = 'field'; - static const argIsPrimary = 'is_primary'; const MobileEditPropertyScreen({ super.key, required this.viewId, required this.field, - this.isPrimary = false, }); final String viewId; final FieldPB field; - final bool isPrimary; @override State createState() => @@ -79,29 +75,25 @@ class _MobileEditPropertyScreenState extends State { } return FieldOptionEditor( mode: FieldOptionMode.edit, - isPrimary: widget.isPrimary, + isPrimary: widget.field.isPrimary, defaultValues: optionValues, onOptionValuesChanged: (optionValues) { this.optionValues = optionValues; }, onAction: (action) { - final service = FieldBackendService( + final service = FieldServices( viewId: viewId, fieldId: fieldId, ); switch (action) { case FieldOptionAction.delete: - service.deleteField(); + service.delete(); break; case FieldOptionAction.duplicate: - service.duplicateField(); + service.duplicate(); break; case FieldOptionAction.hide: - FieldSettingsBackendService(viewId: viewId) - .updateFieldSettings( - fieldId: fieldId, - fieldVisibility: FieldVisibility.AlwaysHidden, - ); + service.hide(); break; } context.pop(); diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart index 5754d6c928..bd3ad9dc1b 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart @@ -6,6 +6,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/base/option_color_list.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options.dart'; +import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/number_format_bloc.dart'; @@ -190,7 +191,7 @@ class _FieldOptionEditorState extends State { child: Column( children: [ const _Divider(), - _OptionTextField( + OptionTextField( controller: controller, type: values.type, onTextChanged: (value) { @@ -289,20 +290,24 @@ class _FieldOptionEditorState extends State { leftIcon: const FlowySvg(FlowySvgs.hide_s), onTap: () => widget.onAction?.call(FieldOptionAction.hide), ), - if (!widget.isPrimary) + if (!widget.isPrimary) ...[ FlowyOptionTile.text( showTopBorder: false, text: LocaleKeys.button_duplicate.tr(), leftIcon: const FlowySvg(FlowySvgs.copy_s), onTap: () => widget.onAction?.call(FieldOptionAction.duplicate), ), - if (!widget.isPrimary) FlowyOptionTile.text( showTopBorder: false, text: LocaleKeys.button_delete.tr(), - leftIcon: const FlowySvg(FlowySvgs.delete_s), + textColor: Theme.of(context).colorScheme.error, + leftIcon: FlowySvg( + FlowySvgs.delete_s, + color: Theme.of(context).colorScheme.error, + ), onTap: () => widget.onAction?.call(FieldOptionAction.delete), ), + ], ] }; } @@ -338,35 +343,6 @@ class _FieldOptionEditorState extends State { } } -class _OptionTextField extends StatelessWidget { - const _OptionTextField({ - required this.controller, - required this.type, - required this.onTextChanged, - }); - - final TextEditingController controller; - final FieldType type; - final void Function(String value) onTextChanged; - - @override - Widget build(BuildContext context) { - return FlowyOptionTile.textField( - controller: controller, - textFieldPadding: const EdgeInsets.symmetric(horizontal: 12.0), - onTextChanged: onTextChanged, - leftIcon: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: FlowySvg( - type.svgData, - size: const Size.square(36.0), - blendMode: null, - ), - ), - ); - } -} - class _PropertyType extends StatelessWidget { const _PropertyType({ required this.type, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart new file mode 100644 index 0000000000..9f044a82f6 --- /dev/null +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/option_text_field.dart @@ -0,0 +1,35 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; +import 'package:appflowy/util/field_type_extension.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; +import 'package:flutter/material.dart'; + +class OptionTextField extends StatelessWidget { + const OptionTextField({ + super.key, + required this.controller, + required this.type, + required this.onTextChanged, + }); + + final TextEditingController controller; + final FieldType type; + final void Function(String value) onTextChanged; + + @override + Widget build(BuildContext context) { + return FlowyOptionTile.textField( + controller: controller, + textFieldPadding: const EdgeInsets.symmetric(horizontal: 12.0), + onTextChanged: onTextChanged, + leftIcon: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: FlowySvg( + type.svgData, + size: const Size.square(36.0), + blendMode: null, + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/widgets.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/widgets.dart index 0bb5b074c2..1eb7e3f4e8 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/widgets.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/widgets.dart @@ -1,3 +1,4 @@ -export 'mobile_field_name_text_field.dart'; export 'mobile_create_field_button.dart'; +export 'mobile_field_name_text_field.dart'; export 'mobile_row_property_list.dart'; +export 'option_text_field.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart index 813d751362..5efdf70bd9 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart @@ -77,15 +77,17 @@ class _MobileDateCellEditScreenState extends State { builder: (_, controller) => Material( child: ColoredBox( color: Theme.of(context).colorScheme.surface, - child: SingleChildScrollView( - controller: controller, - child: Column( - children: [ - const DragHandler(), - _buildHeader(), - _buildBody(), - ], - ), + child: Column( + children: [ + const DragHandler(), + _buildHeader(), + Expanded( + child: SingleChildScrollView( + controller: controller, + child: _buildBody(), + ), + ), + ], ), ), ), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/bottom_sheet_create_field.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/bottom_sheet_create_field.dart index bc0c7a1242..c4de614318 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/bottom_sheet_create_field.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/bottom_sheet_create_field.dart @@ -3,6 +3,7 @@ import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_cr import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart'; +import 'package:appflowy/mobile/presentation/database/field/quick_edit_field_bottom_sheet.dart'; import 'package:appflowy/plugins/database_view/application/field/field_info.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; import 'package:flutter/material.dart'; @@ -43,7 +44,7 @@ void showCreateFieldBottomSheet(BuildContext context, String viewId) { ); } -void showEditFieldScreen( +Future showEditFieldScreen( BuildContext context, String viewId, FieldInfo field, @@ -53,7 +54,6 @@ void showEditFieldScreen( extra: { MobileEditPropertyScreen.argViewId: viewId, MobileEditPropertyScreen.argField: field.field, - MobileEditPropertyScreen.argIsPrimary: field.isPrimary, }, ); if (optionValues != null) { @@ -79,4 +79,30 @@ void showEditFieldScreen( ); } } + + return optionValues; +} + +void showQuickEditField( + BuildContext context, + String viewId, + FieldInfo fieldInfo, +) async { + showMobileBottomSheet( + context, + padding: EdgeInsets.zero, + backgroundColor: Theme.of(context).colorScheme.secondaryContainer, + resizeToAvoidBottomInset: true, + builder: (context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 500), + child: SingleChildScrollView( + child: QuickEditField( + viewId: viewId, + fieldInfo: fieldInfo, + ), + ), + ); + }, + ); } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/field/quick_edit_field_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/quick_edit_field_bottom_sheet.dart new file mode 100644 index 0000000000..bc5ac6f429 --- /dev/null +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/field/quick_edit_field_bottom_sheet.dart @@ -0,0 +1,148 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; +import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart'; +import 'package:appflowy/mobile/presentation/database/field/bottom_sheet_create_field.dart'; +import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; +import 'package:appflowy/plugins/database_view/application/field/field_backend_service.dart'; +import 'package:appflowy/plugins/database_view/application/field/field_info.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class QuickEditField extends StatefulWidget { + const QuickEditField({ + super.key, + required this.viewId, + required this.fieldInfo, + }); + + final String viewId; + final FieldInfo fieldInfo; + + @override + State createState() => _QuickEditFieldState(); +} + +class _QuickEditFieldState extends State { + final TextEditingController controller = TextEditingController(); + + late final FieldServices service = FieldServices( + viewId: widget.viewId, + fieldId: widget.fieldInfo.field.id, + ); + + late FieldType fieldType; + + @override + void initState() { + super.initState(); + + fieldType = widget.fieldInfo.fieldType; + controller.text = widget.fieldInfo.field.name; + } + + @override + void dispose() { + controller.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const AppBarCloseButton(), + OptionTextField( + controller: controller, + type: fieldType, + onTextChanged: (text) async { + await service.updateName(text); + }, + ), + const _Divider(), + FlowyOptionTile.text( + text: LocaleKeys.grid_field_editProperty.tr(), + leftIcon: const FlowySvg(FlowySvgs.edit_s), + onTap: () async { + final optionValues = await showEditFieldScreen( + context, + widget.viewId, + widget.fieldInfo, + ); + if (optionValues != null) { + setState(() { + fieldType = optionValues.type; + controller.text = optionValues.name; + }); + } + }, + ), + FlowyOptionTile.text( + showTopBorder: false, + text: LocaleKeys.grid_field_hide.tr(), + leftIcon: const FlowySvg(FlowySvgs.hide_s), + onTap: () async { + context.pop(); + await service.hide(); + }, + ), + FlowyOptionTile.text( + showTopBorder: false, + text: LocaleKeys.grid_field_insertLeft.tr(), + leftIcon: const FlowySvg(FlowySvgs.insert_left_s), + onTap: () async { + context.pop(); + await service.insertLeft(); + }, + ), + FlowyOptionTile.text( + showTopBorder: false, + text: LocaleKeys.grid_field_insertRight.tr(), + leftIcon: const FlowySvg(FlowySvgs.insert_right_s), + onTap: () async { + context.pop(); + await service.insertRight(); + }, + ), + if (!widget.fieldInfo.isPrimary) ...[ + FlowyOptionTile.text( + showTopBorder: false, + text: LocaleKeys.button_duplicate.tr(), + leftIcon: const FlowySvg(FlowySvgs.copy_s), + onTap: () async { + context.pop(); + await service.duplicate(); + }, + ), + FlowyOptionTile.text( + showTopBorder: false, + text: LocaleKeys.button_delete.tr(), + textColor: Theme.of(context).colorScheme.error, + leftIcon: FlowySvg( + FlowySvgs.delete_s, + color: Theme.of(context).colorScheme.error, + ), + onTap: () async { + context.pop(); + await service.delete(); + }, + ), + ], + ], + ); + } +} + +class _Divider extends StatelessWidget { + const _Divider(); + + @override + Widget build(BuildContext context) { + return const VSpace(20); + } +} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart index 7d4e709d3d..d4786e00c3 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_picker_screen.dart @@ -54,9 +54,7 @@ class _LanguagePickerPageState extends State { LocaleKeys.titleBar_font.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: ListView.separated( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/language/language_picker_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/language/language_picker_screen.dart index f1dcb4d3ea..24ae103574 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/language/language_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/language/language_picker_screen.dart @@ -39,9 +39,7 @@ class _LanguagePickerPageState extends State { LocaleKeys.titleBar_language.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: ListView.separated( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/widgets/flowy_option_tile.dart b/frontend/appflowy_flutter/lib/mobile/presentation/widgets/flowy_option_tile.dart index ea2620243b..4fc0ca020b 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/widgets/flowy_option_tile.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/widgets/flowy_option_tile.dart @@ -17,6 +17,7 @@ class FlowyOptionTile extends StatelessWidget { this.showTopBorder = true, this.showBottomBorder = true, this.text, + this.textColor, this.controller, this.leading, this.onTap, @@ -33,6 +34,7 @@ class FlowyOptionTile extends StatelessWidget { factory FlowyOptionTile.text({ required String text, + Color? textColor, bool showTopBorder = true, bool showBottomBorder = true, Widget? leftIcon, @@ -42,6 +44,7 @@ class FlowyOptionTile extends StatelessWidget { return FlowyOptionTile._( type: FlowyOptionTileType.text, text: text, + textColor: textColor, controller: null, onTap: onTap, showTopBorder: showTopBorder, @@ -128,6 +131,7 @@ class FlowyOptionTile extends StatelessWidget { final bool showTopBorder; final bool showBottomBorder; final String? text; + final Color? textColor; final TextEditingController? controller; final EdgeInsets textFieldPadding; final void Function()? onTap; @@ -156,7 +160,7 @@ class FlowyOptionTile extends StatelessWidget { children: [ _buildText(), ..._buildTextField(), - const Spacer(), + if (controller == null) const Spacer(), trailing ?? const SizedBox.shrink(), const HSpace(12.0), ], @@ -188,6 +192,7 @@ class FlowyOptionTile extends StatelessWidget { useIntrinsicWidth: true, text: FlowyText( text!, + color: textColor, ), margin: const EdgeInsets.symmetric( horizontal: 16.0, @@ -226,7 +231,6 @@ class FlowyOptionTile extends StatelessWidget { child: Container( constraints: const BoxConstraints.tightFor( height: 54.0, - width: double.infinity, ), alignment: Alignment.center, child: TextField( diff --git a/frontend/appflowy_flutter/lib/plugins/base/color/color_picker_screen.dart b/frontend/appflowy_flutter/lib/plugins/base/color/color_picker_screen.dart index 085ff8dcb7..9e1b55ef77 100644 --- a/frontend/appflowy_flutter/lib/plugins/base/color/color_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/plugins/base/color/color_picker_screen.dart @@ -26,9 +26,7 @@ class MobileColorPickerScreen extends StatelessWidget { title ?? LocaleKeys.titleBar_pageIcon.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: FlowyMobileColorPicker( diff --git a/frontend/appflowy_flutter/lib/plugins/base/icon/icon_picker_page.dart b/frontend/appflowy_flutter/lib/plugins/base/icon/icon_picker_page.dart index 6ebf83c1f5..28972045b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/base/icon/icon_picker_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/base/icon/icon_picker_page.dart @@ -4,7 +4,6 @@ import 'package:appflowy/plugins/base/icon/icon_picker.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; class IconPickerPage extends StatelessWidget { const IconPickerPage({ @@ -25,9 +24,7 @@ class IconPickerPage extends StatelessWidget { title ?? LocaleKeys.titleBar_pageIcon.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: FlowyIconPicker( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_backend_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_backend_service.dart new file mode 100644 index 0000000000..8a2120a100 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_backend_service.dart @@ -0,0 +1,69 @@ +import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; +import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart'; +import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; + +// This class is used for combining the +// 1. FieldBackendService +// 2. FieldSettingsBackendService +// 3. TypeOptionBackendService +// +// including, +// hide, delete, duplicated, +// insertLeft, insertRight, +// updateName +class FieldServices { + FieldServices({ + required this.viewId, + required this.fieldId, + }) : fieldBackendService = FieldBackendService( + viewId: viewId, + fieldId: fieldId, + ), + fieldSettingsService = FieldSettingsBackendService( + viewId: viewId, + ); + + final String viewId; + final String fieldId; + + final FieldBackendService fieldBackendService; + final FieldSettingsBackendService fieldSettingsService; + + Future hide() async { + await fieldSettingsService.updateFieldSettings( + fieldId: fieldId, + fieldVisibility: FieldVisibility.AlwaysHidden, + ); + } + + Future delete() async { + await fieldBackendService.deleteField(); + } + + Future duplicate() async { + await fieldBackendService.duplicateField(); + } + + Future insertLeft() async { + await TypeOptionBackendService.createFieldTypeOption( + viewId: viewId, + position: CreateFieldPosition.Before, + targetFieldId: fieldId, + ); + } + + Future insertRight() async { + await TypeOptionBackendService.createFieldTypeOption( + viewId: viewId, + position: CreateFieldPosition.After, + targetFieldId: fieldId, + ); + } + + Future updateName(String name) async { + await fieldBackendService.updateField( + name: name, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/mobile_field_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/mobile_field_cell.dart index 9d1c3333d3..8cd517da8c 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/mobile_field_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/mobile_field_cell.dart @@ -30,7 +30,7 @@ class MobileFieldButton extends StatelessWidget { return SizedBox( width: fieldInfo.fieldSettings!.width.toDouble(), child: FlowyButton( - onTap: () => showEditFieldScreen(context, viewId, fieldInfo), + onTap: () => showQuickEditField(context, viewId, fieldInfo), radius: BorderRadius.zero, margin: const EdgeInsets.symmetric(vertical: 14, horizontal: 12), leftIconSize: const Size.square(18), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart index d58d12cc0c..eacaacfcee 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart @@ -165,6 +165,8 @@ class _DateCellState extends GridCellState { showMobileBottomSheet( context, padding: EdgeInsets.zero, + backgroundColor: + Theme.of(context).colorScheme.secondaryContainer, builder: (context) { return MobileDateCellEditScreen( controller: widget.cellControllerBuilder.build() diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_language_screen.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_language_screen.dart index 1ed08c6e05..2d3cc543ca 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_language_screen.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_language_screen.dart @@ -23,9 +23,7 @@ class MobileCodeLanguagePickerScreen extends StatelessWidget { LocaleKeys.titleBar_language.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: ListView.separated( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart index 82e38ccfe7..9e198cef5d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart @@ -4,7 +4,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; class ImagePickerPage extends StatefulWidget { const ImagePickerPage({ @@ -28,9 +27,7 @@ class _ImagePickerPageState extends State { LocaleKeys.titleBar_pageIcon.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: UploadImageMenu( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_item/mobile_block_settings_screen.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_item/mobile_block_settings_screen.dart index 5a523ddafd..1973076151 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_item/mobile_block_settings_screen.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_item/mobile_block_settings_screen.dart @@ -4,7 +4,6 @@ import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; enum MobileBlockActionType { delete, @@ -74,9 +73,7 @@ class MobileBlockSettingsScreen extends StatelessWidget { LocaleKeys.titleBar_actions.tr(), fontSize: 14.0, ), - leading: AppBarBackButton( - onTap: () => context.pop(), - ), + leading: const AppBarBackButton(), ), body: SafeArea( child: ListView.separated( diff --git a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart index b926c0ae95..96f17c72f1 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart @@ -386,7 +386,6 @@ GoRoute _mobileEditPropertyPageRoute() { child: MobileEditPropertyScreen( viewId: args[MobileEditPropertyScreen.argViewId], field: args[MobileEditPropertyScreen.argField], - isPrimary: args[MobileEditPropertyScreen.argIsPrimary], ), ); }, diff --git a/frontend/resources/flowy_icons/16x/insert_left.svg b/frontend/resources/flowy_icons/16x/insert_left.svg new file mode 100644 index 0000000000..93d987ca9c --- /dev/null +++ b/frontend/resources/flowy_icons/16x/insert_left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/flowy_icons/16x/insert_right.svg b/frontend/resources/flowy_icons/16x/insert_right.svg new file mode 100644 index 0000000000..2f21ed7233 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/insert_right.svg @@ -0,0 +1,5 @@ + + + + +