From 415f1217209f7045da44ad4ccf01e25f009f5cfd Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Sat, 2 Dec 2023 20:39:03 +0800 Subject: [PATCH] feat: edit property (#4065) * feat: edit property * feat: disable actions if field is primary * feat: remove include time * chore: set title to medium * fix: flutter analyze * chore: update built in text style * chore: update text style * fix: unable to click the calendar day --- frontend/appflowy_flutter/ios/Podfile.lock | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../presentation/base/app_bar_actions.dart | 1 - .../bottom_sheet_view_item_header.dart | 12 +- .../show_mobile_bottom_sheet.dart | 1 - .../mobile_create_field_screen.dart | 10 +- .../card_detail/mobile_edit_field_screen.dart | 139 +++++++++ .../card_detail/widgets/_field_options.dart | 6 +- ...option.dart => _field_options_eidtor.dart} | 275 +++++++++--------- .../mobile_date_picker_screen.dart | 8 +- .../mobile_calendar_events_screen.dart | 12 +- .../presentation/home/mobile_home_page.dart | 2 +- .../mobile_home_recent_views.dart | 4 +- .../recent_folder/mobile_recent_view.dart | 6 +- .../page_item/mobile_view_item.dart | 2 +- .../presentation/root_placeholder_page.dart | 29 +- .../widgets/flowy_option_tile.dart | 14 +- .../application/field/field_service.dart | 17 +- .../widgets/header/grid_header.dart | 2 +- .../widgets/header/mobile_field_cell.dart | 54 +++- .../cells/date_cell/mobile_date_editor.dart | 5 +- .../header/document_header_node_widget.dart | 1 + .../document/presentation/editor_style.dart | 5 +- .../more/cubit/document_appearance_cubit.dart | 12 +- .../lib/startup/tasks/generate_router.dart | 21 ++ .../settings/appearance/base_appearance.dart | 37 ++- .../appearance/mobile_appearance.dart | 5 +- .../app_setting_test/appearance_test.dart | 5 +- .../document_appearance_test.dart | 3 +- .../unit_test/editor/editor_style_test.dart | 3 +- .../theme_font_family_setting_test.dart | 5 +- 31 files changed, 447 insertions(+), 253 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart rename frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/{_new_field_option.dart => _field_options_eidtor.dart} (83%) diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index 9c90ae776c..0e508e8ca0 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -202,4 +202,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj index 50fd566bf0..1d08d2be93 100644 --- a/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj +++ b/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj @@ -515,7 +515,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = VHB67HRSZG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AppFlowy; 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 7552de066b..c4be98289b 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 @@ -34,7 +34,6 @@ class AppBarCancelButton extends StatelessWidget { onTap: onTap, child: FlowyText( LocaleKeys.button_cancel.tr(), - fontSize: 16.0, ), ); } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart index 70fb1c6e6f..b6dd615a04 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart @@ -17,7 +17,6 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = Theme.of(context); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -44,9 +43,14 @@ class MobileViewItemBottomSheetHeader extends StatelessWidget { }, ), // title - Text( - view.name, - style: theme.textTheme.labelSmall, + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.6, + ), + child: FlowyText.medium( + view.name, + overflow: TextOverflow.ellipsis, + ), ), const HSpace(24.0), ], 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 8ff7056021..aa9aeb7e86 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 @@ -64,7 +64,6 @@ Future showMobileBottomSheet( : const SizedBox.shrink(), FlowyText( title, - fontSize: 16.0, ), showCloseButton ? HSpace(padding.right + 24) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_create_field_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_create_field_screen.dart index de7a12d4e5..bf174d51c0 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_create_field_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_create_field_screen.dart @@ -1,6 +1,6 @@ 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/_new_field_option.dart'; +import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart'; import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -46,9 +46,8 @@ class _MobileNewPropertyScreenState extends State { return Scaffold( appBar: AppBar( centerTitle: true, - title: FlowyText( + title: FlowyText.medium( LocaleKeys.grid_field_newProperty.tr(), - fontSize: 16.0, ), leading: AppBarCancelButton( onTap: () => context.pop(), @@ -62,7 +61,7 @@ class _MobileNewPropertyScreenState extends State { ), ], ), - body: FieldOption( + body: FieldOptionEditor( mode: FieldOptionMode.add, defaultValues: optionValues, onOptionValuesChanged: (optionValues) { @@ -88,10 +87,9 @@ class _SaveButton extends StatelessWidget { alignment: Alignment.center, child: GestureDetector( onTap: onSave, - child: FlowyText( + child: FlowyText.medium( LocaleKeys.button_save.tr(), color: const Color(0xFF00ADDC), - fontSize: 16.0, ), ), ), 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 new file mode 100644 index 0000000000..8f4c58616b --- /dev/null +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart @@ -0,0 +1,139 @@ +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_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 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() => + _MobileEditPropertyScreenState(); +} + +class _MobileEditPropertyScreenState extends State { + late Future future; + + FieldOptionValues? optionValues; + + @override + void initState() { + super.initState(); + + future = FieldOptionValues.get( + viewId: widget.viewId, + fieldId: widget.field.id, + fieldType: widget.field.fieldType, + ); + } + + @override + Widget build(BuildContext context) { + final viewId = widget.viewId; + final fieldId = widget.field.id; + + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: FlowyText.medium( + LocaleKeys.grid_field_editProperty.tr(), + ), + leading: AppBarCancelButton( + onTap: () => context.pop(), + ), + leadingWidth: 120, + actions: [ + _SaveButton( + onSave: () { + context.pop(optionValues); + }, + ), + ], + ), + body: FutureBuilder( + future: future, + builder: (context, snapshot) { + final optionValues = snapshot.data; + if (optionValues == null) { + return const Center(child: CircularProgressIndicator.adaptive()); + } + return FieldOptionEditor( + mode: FieldOptionMode.edit, + isPrimary: widget.isPrimary, + defaultValues: optionValues, + onOptionValuesChanged: (optionValues) { + this.optionValues = optionValues; + }, + onAction: (action) { + final service = FieldBackendService( + viewId: viewId, + fieldId: fieldId, + ); + switch (action) { + case FieldOptionAction.delete: + service.deleteField(); + break; + case FieldOptionAction.duplicate: + service.duplicateField(); + break; + case FieldOptionAction.hide: + FieldSettingsBackendService(viewId: viewId) + .updateFieldSettings( + fieldId: fieldId, + fieldVisibility: FieldVisibility.AlwaysHidden, + ); + break; + } + context.pop(); + }, + ); + }, + ), + ); + } +} + +class _SaveButton extends StatelessWidget { + const _SaveButton({ + required this.onSave, + }); + + final VoidCallback onSave; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Align( + alignment: Alignment.center, + child: GestureDetector( + onTap: onSave, + child: FlowyText.medium( + LocaleKeys.button_save.tr(), + color: const Color(0xFF00ADDC), + ), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options.dart index 86945d67ba..f4be9a20ff 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options.dart @@ -75,7 +75,7 @@ class _FieldHeader extends StatelessWidget { ), FlowyText.medium( LocaleKeys.titleBar_addField.tr(), - fontSize: 16.0, + fontSize: 17.0, ), const HSpace(120), ], @@ -106,7 +106,9 @@ class _Field extends StatelessWidget { size: Size.square(width / 4.0), ), const VSpace(6.0), - FlowyText(type.i18n), + FlowyText( + type.i18n, + ), ], ), ); diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_new_field_option.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart similarity index 83% rename from frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_new_field_option.dart rename to frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart index ed6804131f..23305f0dd8 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_new_field_option.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart @@ -7,6 +7,7 @@ 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/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'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart'; @@ -32,7 +33,6 @@ class FieldOptionValues { required this.type, required this.name, this.dateFormate, - this.includeTime = false, this.timeFormat, this.numberFormat, this.selectOption = const [], @@ -43,7 +43,6 @@ class FieldOptionValues { // FieldType.Date DateFormatPB? dateFormate; - bool includeTime; TimeFormatPB? timeFormat; // FieldType.Num @@ -56,65 +55,110 @@ class FieldOptionValues { Future create({ required String viewId, }) async { - Uint8List? typeOptionData; - - switch (type) { - case FieldType.RichText: - break; - case FieldType.URL: - break; - case FieldType.Checkbox: - break; - case FieldType.Number: - typeOptionData = NumberTypeOptionPB( - format: numberFormat, - ).writeToBuffer(); - break; - case FieldType.DateTime: - typeOptionData = DateTypeOptionPB( - dateFormat: dateFormate, - timeFormat: timeFormat, - ).writeToBuffer(); - break; - case FieldType.SingleSelect: - typeOptionData = SingleSelectTypeOptionPB( - options: selectOption, - ).writeToBuffer(); - case FieldType.MultiSelect: - typeOptionData = MultiSelectTypeOptionPB( - options: selectOption, - ).writeToBuffer(); - break; - default: - throw UnimplementedError(); - } - await TypeOptionBackendService.createFieldTypeOption( viewId: viewId, fieldType: type, fieldName: name, - typeOptionData: typeOptionData, + typeOptionData: toTypeOptionBuffer(), + ); + } + + Uint8List? toTypeOptionBuffer() { + switch (type) { + case FieldType.RichText: + case FieldType.URL: + case FieldType.Checkbox: + return null; + case FieldType.Number: + return NumberTypeOptionPB( + format: numberFormat, + ).writeToBuffer(); + case FieldType.DateTime: + return DateTypeOptionPB( + dateFormat: dateFormate, + timeFormat: timeFormat, + ).writeToBuffer(); + case FieldType.SingleSelect: + return SingleSelectTypeOptionPB( + options: selectOption, + ).writeToBuffer(); + case FieldType.MultiSelect: + return MultiSelectTypeOptionPB( + options: selectOption, + ).writeToBuffer(); + default: + throw UnimplementedError(); + } + } + + static Future get({ + required String viewId, + required String fieldId, + required FieldType fieldType, + }) async { + final service = FieldBackendService(viewId: viewId, fieldId: fieldId); + final result = await service.getFieldTypeOptionData(fieldType: fieldType); + return result.fold( + (option) { + final type = option.field_2.fieldType; + final buffer = option.typeOptionData; + return FieldOptionValues( + type: type, + name: option.field_2.name, + numberFormat: type == FieldType.Number + ? NumberTypeOptionPB.fromBuffer(buffer).format + : null, + dateFormate: type == FieldType.DateTime + ? DateTypeOptionPB.fromBuffer(buffer).dateFormat + : null, + timeFormat: type == FieldType.DateTime + ? DateTypeOptionPB.fromBuffer(buffer).timeFormat + : null, + selectOption: switch (type) { + FieldType.SingleSelect => + SingleSelectTypeOptionPB.fromBuffer(buffer).options, + FieldType.MultiSelect => + MultiSelectTypeOptionPB.fromBuffer(buffer).options, + _ => [], + }, + ); + }, + (error) => null, ); } } -class FieldOption extends StatefulWidget { - const FieldOption({ +enum FieldOptionAction { + hide, + duplicate, + delete, +} + +class FieldOptionEditor extends StatefulWidget { + const FieldOptionEditor({ super.key, required this.mode, required this.defaultValues, required this.onOptionValuesChanged, + this.onAction, + this.isPrimary = false, }); final FieldOptionMode mode; final FieldOptionValues defaultValues; final void Function(FieldOptionValues values) onOptionValuesChanged; + // only used in edit mode + final void Function(FieldOptionAction action)? onAction; + + // the primary field can't be deleted, duplicated, and changed type + final bool isPrimary; + @override - State createState() => _FieldOptionState(); + State createState() => _FieldOptionEditorState(); } -class _FieldOptionState extends State { +class _FieldOptionEditorState extends State { final controller = TextEditingController(); late FieldOptionValues values; @@ -124,7 +168,7 @@ class _FieldOptionState extends State { super.initState(); values = widget.defaultValues; - controller.text = values.type.i18n; + controller.text = values.name; } @override @@ -136,6 +180,7 @@ class _FieldOptionState extends State { @override Widget build(BuildContext context) { + final option = _buildOption(); return Container( color: Theme.of(context).colorScheme.secondaryContainer, height: MediaQuery.of(context).size.height, @@ -151,18 +196,26 @@ class _FieldOptionState extends State { }, ), const _Divider(), - _PropertyType( - type: values.type, - onSelected: (type) => setState( - () { - controller.text = type.i18n; - _updateOptionValues(type: type, name: type.i18n); - }, + if (!widget.isPrimary) ...[ + _PropertyType( + type: values.type, + onSelected: (type) => setState( + () { + if (widget.mode == FieldOptionMode.add) { + controller.text = type.i18n; + } + _updateOptionValues(type: type, name: type.i18n); + }, + ), ), - ), - const _Divider(), - ..._buildOption(), + const _Divider(), + if (option.isNotEmpty) ...[ + ...option, + const _Divider(), + ], + ], ..._buildOptionActions(), + const _Divider(), ], ), ), @@ -171,18 +224,6 @@ class _FieldOptionState extends State { List _buildOption() { switch (values.type) { - case FieldType.RichText: - return [ - const _TextOption(), - ]; - case FieldType.URL: - return [ - const _URLOption(), - ]; - case FieldType.Checkbox: - return [ - const _CheckboxOption(), - ]; case FieldType.Number: return [ _NumberOption( @@ -204,10 +245,8 @@ class _FieldOptionState extends State { ), const _Divider(), _TimeOption( - includeTime: values.includeTime, selectedFormat: values.timeFormat ?? TimeFormatPB.TwelveHour, - onSelected: (includeTime, format) => _updateOptionValues( - includeTime: includeTime, + onSelected: (format) => _updateOptionValues( timeFormat: format, ), ), @@ -244,19 +283,24 @@ class _FieldOptionState extends State { FieldOptionMode.add => [], FieldOptionMode.edit => [ FlowyOptionTile.text( - text: LocaleKeys.button_delete.tr(), - leftIcon: const FlowySvg(FlowySvgs.delete_s), - ), - FlowyOptionTile.text( - showTopBorder: false, - text: LocaleKeys.button_duplicate.tr(), - leftIcon: const FlowySvg(FlowySvgs.copy_s), - ), - FlowyOptionTile.text( - showTopBorder: false, text: LocaleKeys.grid_field_hide.tr(), leftIcon: const FlowySvg(FlowySvgs.hide_s), + onTap: () => widget.onAction?.call(FieldOptionAction.hide), ), + 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), + onTap: () => widget.onAction?.call(FieldOptionAction.delete), + ), ] }; } @@ -265,7 +309,6 @@ class _FieldOptionState extends State { FieldType? type, String? name, DateFormatPB? dateFormate, - bool? includeTime, TimeFormatPB? timeFormat, NumberFormatPB? numberFormat, List? selectOption, @@ -279,9 +322,6 @@ class _FieldOptionState extends State { if (dateFormate != null) { values.dateFormate = dateFormate; } - if (includeTime != null) { - values.includeTime = includeTime; - } if (timeFormat != null) { values.timeFormat = timeFormat; } @@ -348,7 +388,6 @@ class _PropertyType extends StatelessWidget { FlowyText( type.i18n, color: Theme.of(context).hintColor, - fontSize: 16.0, ), const HSpace(4.0), FlowySvg( @@ -394,33 +433,6 @@ class _Divider extends StatelessWidget { } } -class _TextOption extends StatelessWidget { - const _TextOption(); - - @override - Widget build(BuildContext context) { - return const SizedBox.shrink(); - } -} - -class _URLOption extends StatelessWidget { - const _URLOption(); - - @override - Widget build(BuildContext context) { - return const SizedBox.shrink(); - } -} - -class _CheckboxOption extends StatelessWidget { - const _CheckboxOption(); - - @override - Widget build(BuildContext context) { - return const SizedBox.shrink(); - } -} - class _DateOption extends StatefulWidget { const _DateOption({ required this.selectedFormat, @@ -456,7 +468,6 @@ class _DateOptionState extends State<_DateOption> { ), child: FlowyText( LocaleKeys.grid_field_dateFormat.tr(), - fontSize: 16.0, color: Theme.of(context).hintColor, ), ), @@ -480,14 +491,12 @@ class _DateOptionState extends State<_DateOption> { class _TimeOption extends StatefulWidget { const _TimeOption({ - required this.includeTime, required this.selectedFormat, required this.onSelected, }); - final bool includeTime; final TimeFormatPB selectedFormat; - final Function(bool includeTime, TimeFormatPB format) onSelected; + final Function(TimeFormatPB format) onSelected; @override State<_TimeOption> createState() => _TimeOptionState(); @@ -495,14 +504,12 @@ class _TimeOption extends StatefulWidget { class _TimeOptionState extends State<_TimeOption> { TimeFormatPB selectedFormat = TimeFormatPB.TwelveHour; - bool includeTime = false; @override void initState() { super.initState(); selectedFormat = widget.selectedFormat; - includeTime = widget.includeTime; } @override @@ -517,34 +524,22 @@ class _TimeOptionState extends State<_TimeOption> { ), child: FlowyText( LocaleKeys.grid_field_timeFormat.tr(), - fontSize: 16.0, color: Theme.of(context).hintColor, ), ), - FlowyOptionTile.switcher( - text: LocaleKeys.grid_field_includeTime.tr(), - isSelected: includeTime, - onValueChanged: (includeTime) { - widget.onSelected(includeTime, selectedFormat); - setState(() { - this.includeTime = includeTime; - }); - }, - ), - if (includeTime) - ...TimeFormatPB.values.mapIndexed((index, format) { - return FlowyOptionTile.checkbox( - text: format.title(), - isSelected: selectedFormat == format, - showTopBorder: false, - onTap: () { - widget.onSelected(includeTime, format); - setState(() { - selectedFormat = format; - }); - }, - ); - }), + ...TimeFormatPB.values.mapIndexed((index, format) { + return FlowyOptionTile.checkbox( + text: format.title(), + isSelected: selectedFormat == format, + showTopBorder: false, + onTap: () { + widget.onSelected(format); + setState(() { + selectedFormat = format; + }); + }, + ); + }), ], ); } @@ -569,7 +564,6 @@ class _NumberOption extends StatelessWidget { FlowyText( selectedFormat.title(), color: Theme.of(context).hintColor, - fontSize: 16.0, ), const HSpace(4.0), FlowySvg( @@ -663,7 +657,6 @@ class _SelectOption extends StatelessWidget { ), child: FlowyText( LocaleKeys.grid_field_optionTitle.tr(), - fontSize: 16.0, color: Theme.of(context).hintColor, ), ), 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 a29f43cd86..782ff0cd32 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 @@ -58,7 +58,9 @@ class _MobileDateCellEditScreenState extends State { Widget _buildFullScreen() { return Scaffold( appBar: AppBar( - title: Text(LocaleKeys.titleBar_date.tr()), + title: FlowyText.medium( + LocaleKeys.titleBar_date.tr(), + ), ), body: _buildBody(), ); @@ -117,7 +119,7 @@ class _MobileDateCellEditScreenState extends State { } Widget _buildHeader() { - const iconWidth = 36.0; + const iconWidth = 30.0; const height = 44.0; return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), @@ -139,7 +141,7 @@ class _MobileDateCellEditScreenState extends State { alignment: Alignment.center, child: FlowyText.medium( LocaleKeys.grid_field_dateFieldName.tr(), - fontSize: 18, + fontSize: 16, ), ), ].map((e) => SizedBox(height: height, child: e)).toList(), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/mobile_calendar_events_screen.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/mobile_calendar_events_screen.dart index 52884d9888..c15ea8e0d9 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/mobile_calendar_events_screen.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/mobile_calendar_events_screen.dart @@ -9,14 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class MobileCalendarEventsScreen extends StatefulWidget { - static const routeName = "/calendar-events"; + static const routeName = '/calendar_events'; // GoRouter Arguments - static const calendarBlocKey = "calendar_bloc"; - static const calendarDateKey = "date"; - static const calendarEventsKey = "events"; - static const calendarRowCacheKey = "row_cache"; - static const calendarViewIdKey = "view_id"; + static const calendarBlocKey = 'calendar_bloc'; + static const calendarDateKey = 'date'; + static const calendarEventsKey = 'events'; + static const calendarRowCacheKey = 'row_cache'; + static const calendarViewIdKey = 'view_id'; const MobileCalendarEventsScreen({ super.key, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page.dart index 05b69e63cb..3cc2e6d62e 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page.dart @@ -137,7 +137,7 @@ class _TrashButton extends StatelessWidget { color: Theme.of(context).colorScheme.onSurface, ), leftIconSize: const Size.square(24), - text: FlowyText( + text: FlowyText.medium( LocaleKeys.trash_text.tr(), fontSize: 18.0, ), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart index 65740156fc..1066eba755 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart @@ -80,8 +80,8 @@ class _RecentViews extends StatelessWidget { separatorBuilder: () => const HSpace(8), children: recentViews .map( - (view) => SizedBox( - width: 150, + (view) => SizedBox.square( + dimension: 148, child: MobileRecentView(view: view), ), ) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_recent_view.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_recent_view.dart index 01d5dee7ce..1f8ef5b2cf 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_recent_view.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_recent_view.dart @@ -96,8 +96,8 @@ class _MobileRecentViewState extends State { ), Expanded( child: Padding( - padding: const EdgeInsets.fromLTRB(8, 16, 8, 2), - child: FlowyText( + padding: const EdgeInsets.fromLTRB(8, 18, 8, 2), + child: FlowyText.medium( view.name, maxLines: 2, overflow: TextOverflow.ellipsis, @@ -110,7 +110,7 @@ class _MobileRecentViewState extends State { Align( alignment: Alignment.centerLeft, child: Padding( - padding: const EdgeInsets.only(left: 4), + padding: const EdgeInsets.only(left: 8.0), child: icon.isNotEmpty ? EmojiText( emoji: icon, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart b/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart index f869e4532a..d4f152f0dd 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart @@ -307,7 +307,7 @@ class _SingleMobileInnerViewItemState extends State { const HSpace(8), // title Expanded( - child: FlowyText.regular( + child: FlowyText.medium( widget.view.name, fontSize: 18.0, overflow: TextOverflow.ellipsis, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/root_placeholder_page.dart b/frontend/appflowy_flutter/lib/mobile/presentation/root_placeholder_page.dart index 6092177f2c..7465ccaa34 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/root_placeholder_page.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/root_placeholder_page.dart @@ -1,5 +1,5 @@ +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; /// Widget for the root/initial pages in the bottom navigation bar. class RootPlaceholderScreen extends StatelessWidget { @@ -24,31 +24,10 @@ class RootPlaceholderScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Root of section $label'), - ), - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('$label Page', style: Theme.of(context).textTheme.titleLarge), - const Padding(padding: EdgeInsets.all(4)), - TextButton( - onPressed: () { - context.go(detailsPath, extra: '$label-XYZ'); - }, - child: const Text('View details'), - ), - const Padding(padding: EdgeInsets.all(4)), - if (secondDetailsPath != null) - TextButton( - onPressed: () { - context.go(secondDetailsPath!); - }, - child: const Text('View more details'), - ), - ], - ), + centerTitle: true, + title: FlowyText.medium(label), ), + body: const SizedBox.shrink(), ); } } 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 d0a3a31e52..ea2620243b 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 @@ -22,7 +22,8 @@ class FlowyOptionTile extends StatelessWidget { this.onTap, this.trailing, this.textFieldPadding = const EdgeInsets.symmetric( - horizontal: 16.0, + horizontal: 12.0, + vertical: 2.0, ), this.isSelected = false, this.textFieldHintText, @@ -55,7 +56,8 @@ class FlowyOptionTile extends StatelessWidget { void Function(String value)? onTextChanged, void Function(String value)? onTextSubmitted, EdgeInsets textFieldPadding = const EdgeInsets.symmetric( - horizontal: 16.0, + horizontal: 12.0, + vertical: 2.0, ), bool showTopBorder = true, bool showBottomBorder = true, @@ -150,6 +152,7 @@ class FlowyOptionTile extends StatelessWidget { showTopBorder: showTopBorder, showBottomBorder: showBottomBorder, child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildText(), ..._buildTextField(), @@ -185,7 +188,6 @@ class FlowyOptionTile extends StatelessWidget { useIntrinsicWidth: true, text: FlowyText( text!, - fontSize: 16.0, ), margin: const EdgeInsets.symmetric( horizontal: 16.0, @@ -206,7 +208,6 @@ class FlowyOptionTile extends StatelessWidget { ), child: FlowyText( text!, - fontSize: 16.0, ), ); } @@ -222,11 +223,12 @@ class FlowyOptionTile extends StatelessWidget { return [ if (leading != null) leading!, Expanded( - child: ConstrainedBox( + child: Container( constraints: const BoxConstraints.tightFor( - height: 52.0, + height: 54.0, width: double.infinity, ), + alignment: Alignment.center, child: TextField( controller: controller, textInputAction: TextInputAction.done, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart index 4fbd2a8047..5932d4fe09 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart @@ -1,8 +1,8 @@ -import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart'; -import 'package:dartz/dartz.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; -import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; +import 'package:dartz/dartz.dart'; /// FieldService consists of lots of event functions. We define the events in the backend(Rust), /// you can find the corresponding event implementation in event_map.rs of the corresponding crate. @@ -56,6 +56,17 @@ class FieldBackendService { return DatabaseEventUpdateFieldTypeOption(payload).send(); } + Future> updateFieldType({ + required FieldType fieldType, + }) { + final payload = UpdateFieldTypePayloadPB.create() + ..viewId = viewId + ..fieldId = fieldId + ..fieldType = fieldType; + + return DatabaseEventUpdateFieldType(payload).send(); + } + Future> deleteField() { final payload = DeleteFieldPayloadPB.create() ..viewId = viewId diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart index 0733f73a4b..46dd57c82a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart @@ -3,7 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_create_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/_new_field_option.dart'; +import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/_field_options_eidtor.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart'; import 'package:appflowy/plugins/database_view/grid/application/grid_header_bloc.dart'; 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 beeb0510b8..3da35d33d9 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 @@ -1,12 +1,12 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_database_field_editor.dart'; -import 'package:appflowy/mobile/presentation/widgets/flowy_paginated_bottom_sheet.dart'; -import 'package:appflowy/plugins/database_view/application/field/field_info.dart'; -import 'package:easy_localization/easy_localization.dart'; +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_eidtor.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.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:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'field_type_extension.dart'; @@ -41,19 +41,41 @@ class MobileFieldButton extends StatelessWidget { ), ), child: FlowyButton( - onTap: () { - showPaginatedBottomSheet( - context, - page: SheetPage( - title: LocaleKeys.grid_field_editProperty.tr(), - body: MobileDBBottomSheetFieldEditor( - viewId: viewId, - field: fieldInfo.field, - fieldController: fieldController, - ), - ), + onTap: () async { + final optionValues = await context.push( + MobileEditPropertyScreen.routeName, + extra: { + MobileEditPropertyScreen.argViewId: viewId, + MobileEditPropertyScreen.argField: fieldInfo.field, + MobileEditPropertyScreen.argIsPrimary: fieldInfo.isPrimary, + }, ); + if (optionValues != null) { + final fieldId = fieldInfo.field.id; + final service = FieldBackendService( + viewId: viewId, + fieldId: fieldId, + ); + + if (optionValues.name != fieldInfo.name) { + await service.updateField(name: optionValues.name); + } + + if (optionValues.type != fieldInfo.fieldType) { + await service.updateFieldType(fieldType: optionValues.type); + } + + final data = optionValues.toTypeOptionBuffer(); + if (data != null) { + await FieldBackendService.updateFieldTypeOption( + viewId: viewId, + fieldId: fieldId, + typeOptionData: data, + ); + } + } }, + radius: BorderRadius.zero, margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), leftIcon: FlowySvg( fieldInfo.fieldType.icon(), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/mobile_date_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/mobile_date_editor.dart index 61775ad471..f676937885 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/mobile_date_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/mobile_date_editor.dart @@ -41,9 +41,7 @@ class _MobileDatePickerState extends State { Widget _buildCalendar(BuildContext context) { const selectedColor = Color(0xFF00BCF0); - final textStyle = Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16.0, - ); + final textStyle = Theme.of(context).textTheme.bodyMedium!.copyWith(); const boxDecoration = BoxDecoration( shape: BoxShape.circle, ); @@ -156,7 +154,6 @@ class _MobileDatePickerState extends State { builder: (_, value, ___) { return FlowyText( DateFormat.yMMMM(value.$2).format(value.$1), - fontSize: 16.0, ); }, ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart index 49ef85a1ac..2d8c53b31d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart @@ -426,6 +426,7 @@ class DocumentCoverState extends State { children: [ IntrinsicWidth( child: RoundedTextButton( + fontSize: 14, onPressed: () { showMobileBottomSheet( context, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index 18eb383581..2cff5cf8d6 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -6,6 +6,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_too import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/util/google_font_family_extension.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:collection/collection.dart'; import 'package:flutter/gestures.dart'; @@ -160,7 +161,7 @@ class EditorStyleCustomizer { final theme = Theme.of(context); final fontSize = context.read().state.fontSize; return TextStyle( - fontFamily: 'poppins', + fontFamily: builtInFontFamily, fontSize: fontSize, height: 1.5, color: theme.colorScheme.onBackground.withOpacity(0.6), @@ -207,7 +208,7 @@ class EditorStyleCustomizer { fontWeight: fontWeight, ); } on Exception { - return GoogleFonts.getFont('Poppins'); + return GoogleFonts.getFont(builtInFontFamily); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/cubit/document_appearance_cubit.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/cubit/document_appearance_cubit.dart index bbe352ae22..558f683686 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/more/cubit/document_appearance_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/more/cubit/document_appearance_cubit.dart @@ -1,4 +1,5 @@ import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:bloc/bloc.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -28,14 +29,19 @@ class DocumentAppearance { class DocumentAppearanceCubit extends Cubit { DocumentAppearanceCubit() - : super(const DocumentAppearance(fontSize: 16.0, fontFamily: 'Poppins')); + : super( + const DocumentAppearance( + fontSize: 16.0, + fontFamily: builtInFontFamily, + ), + ); Future fetch() async { final prefs = await SharedPreferences.getInstance(); final fontSize = prefs.getDouble(KVKeys.kDocumentAppearanceFontSize) ?? 16.0; - final fontFamily = - prefs.getString(KVKeys.kDocumentAppearanceFontFamily) ?? 'Poppins'; + final fontFamily = prefs.getString(KVKeys.kDocumentAppearanceFontFamily) ?? + builtInFontFamily; final defaultTextDirection = prefs.getString(KVKeys.kDocumentAppearanceDefaultTextDirection); diff --git a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart index 493d1de51b..b926c0ae95 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart @@ -2,6 +2,7 @@ import 'package:appflowy/mobile/presentation/database/board/mobile_board_screen. import 'package:appflowy/mobile/presentation/database/card/card.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_create_field_screen.dart'; import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_create_row_field_screen.dart'; +import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_edit_field_screen.dart'; import 'package:appflowy/mobile/presentation/database/card/card_property_edit/card_property_edit_screen.dart'; import 'package:appflowy/mobile/presentation/database/date_picker/mobile_date_picker_screen.dart'; import 'package:appflowy/mobile/presentation/database/mobile_calendar_events_screen.dart'; @@ -64,6 +65,7 @@ GoRouter generateRouter(Widget child) { _mobileDateCellEditScreenRoute(), _mobileCreateRowFieldScreenRoute(), _mobileNewPropertyPageRoute(), + _mobileEditPropertyPageRoute(), // home // MobileHomeSettingPage is outside the bottom navigation bar, thus it is not in the StatefulShellRoute. @@ -373,9 +375,28 @@ GoRoute _mobileNewPropertyPageRoute() { ); } +GoRoute _mobileEditPropertyPageRoute() { + return GoRoute( + parentNavigatorKey: AppGlobals.rootNavKey, + path: MobileEditPropertyScreen.routeName, + pageBuilder: (context, state) { + final args = state.extra as Map; + return MaterialPage( + fullscreenDialog: true, + child: MobileEditPropertyScreen( + viewId: args[MobileEditPropertyScreen.argViewId], + field: args[MobileEditPropertyScreen.argField], + isPrimary: args[MobileEditPropertyScreen.argIsPrimary], + ), + ); + }, + ); +} + GoRoute _mobileCalendarEventsPageRoute() { return GoRoute( path: MobileCalendarEventsScreen.routeName, + parentNavigatorKey: AppGlobals.rootNavKey, pageBuilder: (context, state) { final args = state.extra as Map; diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/base_appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/base_appearance.dart index a68c54cd86..6c75211247 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/base_appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/base_appearance.dart @@ -3,6 +3,8 @@ import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +const builtInFontFamily = 'Poppins'; + abstract class BaseAppearance { final white = const Color(0xFFFFFFFF); @@ -20,25 +22,36 @@ abstract class BaseAppearance { double? letterSpacing, double? lineHeight, }) { + fontSize = fontSize ?? FontSizes.s12; + fontWeight = fontWeight ?? FontWeight.w400; + letterSpacing = fontSize * (letterSpacing ?? 0.005); + + final textStyle = TextStyle( + fontFamily: fontFamily, + fontSize: fontSize, + color: fontColor, + fontWeight: fontWeight, + fontFamilyFallback: const [builtInFontFamily], + letterSpacing: letterSpacing, + height: lineHeight, + ); + + // we embed Poppins font in the app, so we can use it without GoogleFonts + if (fontFamily == builtInFontFamily) { + return textStyle; + } + try { return GoogleFonts.getFont( fontFamily, - fontSize: fontSize ?? FontSizes.s12, + fontSize: fontSize, color: fontColor, - fontWeight: fontWeight ?? FontWeight.w500, - letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005), + fontWeight: fontWeight, + letterSpacing: letterSpacing, height: lineHeight, ); } catch (e) { - return TextStyle( - fontFamily: fontFamily, - fontSize: fontSize ?? FontSizes.s12, - color: fontColor, - fontWeight: fontWeight ?? FontWeight.w500, - fontFamilyFallback: const ['Poppins'], - letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005), - height: lineHeight, - ); + return textStyle; } } diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart index 44a90b8d9b..4b8e90fdb0 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/mobile_appearance.dart @@ -24,7 +24,10 @@ class MobileAppearance extends BaseAppearance { final fontStyle = getFontStyle( fontFamily: fontFamily, + fontSize: 16.0, + fontWeight: FontWeight.w400, ); + final codeFontStyle = getFontStyle( fontFamily: codeFontFamily, ); @@ -196,9 +199,7 @@ class MobileAppearance extends BaseAppearance { // body2 14 Regular bodyMedium: fontStyle.copyWith( color: colorTheme.onBackground, - fontSize: 14, fontWeight: FontWeight.w400, - // height: 1.2, letterSpacing: 0.07, ), // Trash empty title diff --git a/frontend/appflowy_flutter/test/bloc_test/app_setting_test/appearance_test.dart b/frontend/appflowy_flutter/test/bloc_test/app_setting_test/appearance_test.dart index 406639f897..179154b20b 100644 --- a/frontend/appflowy_flutter/test/bloc_test/app_setting_test/appearance_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/app_setting_test/appearance_test.dart @@ -1,7 +1,8 @@ import 'package:appflowy/user/application/user_settings_service.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; -import 'package:bloc_test/bloc_test.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart'; +import 'package:bloc_test/bloc_test.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -35,7 +36,7 @@ void main() { AppTheme.fallback, ), verify: (bloc) { - expect(bloc.state.font, 'Poppins'); + expect(bloc.state.font, builtInFontFamily); expect(bloc.state.monospaceFont, 'SF Mono'); expect(bloc.state.themeMode, ThemeMode.system); }, diff --git a/frontend/appflowy_flutter/test/bloc_test/app_setting_test/document_appearance_test.dart b/frontend/appflowy_flutter/test/bloc_test/app_setting_test/document_appearance_test.dart index ccece67e93..6c59b29137 100644 --- a/frontend/appflowy_flutter/test/bloc_test/app_setting_test/document_appearance_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/app_setting_test/document_appearance_test.dart @@ -1,5 +1,6 @@ import 'package:appflowy/core/config/kv_keys.dart'; import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -26,7 +27,7 @@ void main() { test('Initial state', () { expect(cubit.state.fontSize, 16.0); - expect(cubit.state.fontFamily, 'Poppins'); + expect(cubit.state.fontFamily, builtInFontFamily); }); test('Fetch document appearance from SharedPreferences', () async { diff --git a/frontend/appflowy_flutter/test/unit_test/editor/editor_style_test.dart b/frontend/appflowy_flutter/test/unit_test/editor/editor_style_test.dart index 6d0c65da60..f15bd16b93 100644 --- a/frontend/appflowy_flutter/test/unit_test/editor/editor_style_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/editor/editor_style_test.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -39,7 +40,7 @@ void main() { expect(result, isA()); expect( result.fontFamily, - GoogleFonts.getFont('Poppins').fontFamily, + GoogleFonts.getFont(builtInFontFamily).fontFamily, ); }); }); diff --git a/frontend/appflowy_flutter/test/widget_test/theme_font_family_setting_test.dart b/frontend/appflowy_flutter/test/widget_test/theme_font_family_setting_test.dart index 52b42f7445..7c89fd3d7d 100644 --- a/frontend/appflowy_flutter/test/widget_test/theme_font_family_setting_test.dart +++ b/frontend/appflowy_flutter/test/widget_test/theme_font_family_setting_test.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; @@ -57,7 +58,7 @@ void main() { ], child: const Scaffold( body: ThemeFontFamilySetting( - currentFontFamily: 'Poppins', + currentFontFamily: builtInFontFamily, ), ), ), @@ -70,7 +71,7 @@ void main() { await tester.pumpAndSettle(); // Verify the initial font family - expect(find.text('Poppins'), findsAtLeastNWidgets(1)); + expect(find.text(builtInFontFamily), findsAtLeastNWidgets(1)); when(() => appearanceSettingsCubit.setFontFamily(any())) .thenAnswer((_) async {}); verifyNever(() => appearanceSettingsCubit.setFontFamily(any()));