diff --git a/frontend/appflowy_flutter/integration_test/desktop/uncategorized/appearance_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/appearance_settings_test.dart index b968ccfd8d..4b7848fd08 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/uncategorized/appearance_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/appearance_settings_test.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/settings_appearance.dart'; @@ -82,8 +83,10 @@ void main() { await tester.openSettingsPage(SettingsPage.appearance); expect( - find.textContaining(DefaultAppearanceSettings.kDefaultFontFamily), - findsOneWidget, + find.textContaining( + DefaultAppearanceSettings.kDefaultFontFamily.fontFamilyDisplayName, + ), + findsNWidgets(2), ); }); }); diff --git a/frontend/appflowy_flutter/lib/mobile/application/page_style/document_page_style_bloc.dart b/frontend/appflowy_flutter/lib/mobile/application/page_style/document_page_style_bloc.dart index b08d62c9be..52552fce3b 100644 --- a/frontend/appflowy_flutter/lib/mobile/application/page_style/document_page_style_bloc.dart +++ b/frontend/appflowy_flutter/lib/mobile/application/page_style/document_page_style_bloc.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy_backend/log.dart'; @@ -179,8 +178,8 @@ class DocumentPageStyleBloc ); } - String _getSelectedFontFamily(Map layoutObject) { - return layoutObject[ViewExtKeys.fontKey] ?? builtInFontFamily(); + String? _getSelectedFontFamily(Map layoutObject) { + return layoutObject[ViewExtKeys.fontKey]; } (PageStyleCoverImageType, String colorValue) _getSelectedCover( 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 57be077b01..35b3ece7c5 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 @@ -216,7 +216,11 @@ class _MobileViewPageState extends State { child: AppBarButton( padding: EdgeInsets.zero, onTap: (context) => context.pop(), - child: _buildImmersiveAppBarIcon(FlowySvgs.m_app_bar_back_s), + child: _buildImmersiveAppBarIcon( + FlowySvgs.m_app_bar_back_s, + 30.0, + iconPadding: 6.0, + ), ), ), actions: actions, @@ -274,13 +278,13 @@ class _MobileViewPageState extends State { ), ); }, - child: _buildImmersiveAppBarIcon(FlowySvgs.m_layout_s), + child: _buildImmersiveAppBarIcon(FlowySvgs.m_layout_s, 30.0), ); } Widget _buildAppBarMoreButton(ViewPB view) { return AppBarButton( - padding: const EdgeInsets.only(left: 8, right: 16, top: 2, bottom: 2), + padding: const EdgeInsets.only(left: 8, right: 16), onTap: (context) { EditorNotification.exitEditing().post(); @@ -292,49 +296,62 @@ class _MobileViewPageState extends State { builder: (_) => _buildAppBarMoreBottomSheet(context), ); }, - child: _buildImmersiveAppBarIcon(FlowySvgs.m_app_bar_more_s), + child: _buildImmersiveAppBarIcon(FlowySvgs.m_app_bar_more_s, 30.0), ); } - Widget _buildImmersiveAppBarIcon(FlowySvgData icon) { - return ValueListenableBuilder( - valueListenable: _isImmersiveMode, - builder: (context, isImmersiveMode, child) { - return ValueListenableBuilder( - valueListenable: _appBarOpacity, - builder: (context, appBarOpacity, child) { - Color? color; + Widget _buildImmersiveAppBarIcon( + FlowySvgData icon, + double dimension, { + double iconPadding = 5.0, + }) { + assert( + dimension > 0.0 && dimension <= kToolbarHeight, + 'dimension must be greater than 0, and less than or equal to kToolbarHeight', + ); + return UnconstrainedBox( + child: SizedBox.square( + dimension: dimension, + child: ValueListenableBuilder( + valueListenable: _isImmersiveMode, + builder: (context, isImmersiveMode, child) { + return ValueListenableBuilder( + valueListenable: _appBarOpacity, + builder: (context, appBarOpacity, child) { + Color? color; - // if there's no cover or the cover is not immersive, - // make sure the app bar is always visible - if (!isImmersiveMode) { - color = null; - } else if (appBarOpacity < 0.99) { - color = Colors.white; - } + // if there's no cover or the cover is not immersive, + // make sure the app bar is always visible + if (!isImmersiveMode) { + color = null; + } else if (appBarOpacity < 0.99) { + color = Colors.white; + } - Widget child = Container( - margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - child: FlowySvg( - icon, - color: color, - ), + Widget child = Container( + margin: EdgeInsets.all(iconPadding), + child: FlowySvg( + icon, + color: color, + ), + ); + + if (isImmersiveMode && appBarOpacity <= 0.99) { + child = DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(dimension / 2.0), + color: Colors.black.withOpacity(0.2), + ), + child: child, + ); + } + + return child; + }, ); - - if (isImmersiveMode && appBarOpacity <= 0.99) { - child = DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22), - color: Colors.black.withOpacity(0.2), - ), - child: child, - ); - } - - return child; }, - ); - }, + ), + ), ); } 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 93e89d7c29..64dd62729c 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 @@ -3,7 +3,7 @@ import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart'; import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_search_text_field.dart'; import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; import 'package:appflowy/shared/google_fonts_extension.dart'; -import 'package:appflowy/util/google_font_family_extension.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -13,7 +13,7 @@ import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; final List _availableFonts = [ - builtInFontFamily(), + defaultFontFamily, ...GoogleFonts.asMap().keys, ]; @@ -106,16 +106,12 @@ class _FontSelectorState extends State { } final fontFamilyName = availableFonts[index - 1]; - final usingDefaultFontFamily = fontFamilyName == builtInFontFamily(); + final usingDefaultFontFamily = fontFamilyName == defaultFontFamily; final fontFamily = !usingDefaultFontFamily ? getGoogleFontSafely(fontFamilyName).fontFamily - : TextStyle(fontFamily: builtInFontFamily()).fontFamily; + : defaultFontFamily; return FlowyOptionTile.checkbox( - // display the default font name if the font family name is empty - // or using the default font family - text: fontFamilyName.isNotEmpty && !usingDefaultFontFamily - ? fontFamilyName.parseFontFamilyName() - : LocaleKeys.settings_appearance_fontFamily_defaultFont.tr(), + text: fontFamilyName.fontFamilyDisplayName, isSelected: widget.selectedFontFamilyName == fontFamilyName, showTopBorder: false, onTap: () => widget.onFontFamilySelected(fontFamilyName), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_setting.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_setting.dart index 8e3827bf94..050bf4b594 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_setting.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/font/font_setting.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; -import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; @@ -22,9 +22,7 @@ class FontSetting extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final selectedFont = context.watch().state.font; - final name = selectedFont == builtInFontFamily() - ? LocaleKeys.settings_appearance_fontFamily_defaultFont.tr() - : selectedFont; + final name = selectedFont.fontFamilyDisplayName; return MobileSettingItem( name: LocaleKeys.settings_appearance_fontFamily_label.tr(), trailing: Row( diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/document_appearance_cubit.dart b/frontend/appflowy_flutter/lib/plugins/document/application/document_appearance_cubit.dart index 3a39f0ed56..1a39c519a3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/document_appearance_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/document_appearance_cubit.dart @@ -57,9 +57,9 @@ class DocumentAppearance { class DocumentAppearanceCubit extends Cubit { DocumentAppearanceCubit() : super( - DocumentAppearance( + const DocumentAppearance( fontSize: 16.0, - fontFamily: builtInFontFamily(), + fontFamily: defaultFontFamily, codeFontFamily: builtInCodeFontFamily, ), ); @@ -69,7 +69,7 @@ class DocumentAppearanceCubit extends Cubit { final fontSize = prefs.getDouble(KVKeys.kDocumentAppearanceFontSize) ?? 16.0; final fontFamily = prefs.getString(KVKeys.kDocumentAppearanceFontFamily) ?? - builtInFontFamily(); + defaultFontFamily; final defaultTextDirection = prefs.getString(KVKeys.kDocumentAppearanceDefaultTextDirection); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart index fd0a5a4885..e25f32f55f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart @@ -127,7 +127,7 @@ class _DocumentImmersiveCoverState extends State { BuildContext context, DocumentImmersiveCoverState state, ) { - String? fontFamily = builtInFontFamily(); + String? fontFamily = defaultFontFamily; final documentFontFamily = context.read().state.fontFamily; if (documentFontFamily != null && fontFamily != documentFontFamily) { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart index 8c727ac4d5..d4d94be091 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart @@ -1,6 +1,8 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/permission/permission_checker.dart'; import 'package:appflowy/startup/startup.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -20,22 +22,28 @@ class UploadImageFileWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return FlowyHover( - child: FlowyButton( - showDefaultBoxDecorationOnMobile: true, - text: Container( - margin: const EdgeInsets.all(4.0), - alignment: Alignment.center, - child: FlowyText( - LocaleKeys.document_imageBlock_upload_placeholder.tr(), - ), + final child = FlowyButton( + showDefaultBoxDecorationOnMobile: true, + text: Container( + margin: const EdgeInsets.all(4.0), + alignment: Alignment.center, + child: FlowyText( + LocaleKeys.document_imageBlock_upload_placeholder.tr(), ), - onTap: _uploadImage, ), + onTap: () => _uploadImage(context), ); + + if (PlatformExtension.isDesktopOrWeb) { + return FlowyHover( + child: child, + ); + } + + return child; } - Future _uploadImage() async { + Future _uploadImage(BuildContext context) async { if (PlatformExtension.isDesktopOrWeb) { // on desktop, the users can pick a image file from folder final result = await getIt().pickFiles( @@ -45,6 +53,12 @@ class UploadImageFileWidget extends StatelessWidget { ); onPickFile(result?.files.firstOrNull?.path); } else { + final photoPermission = + await PermissionChecker.checkPhotoPermission(context); + if (!photoPermission) { + Log.error('Has no permission to access the photo library'); + return; + } // on mobile, the users can pick a image file from camera or image library final result = await ImagePicker().pickImage(source: ImageSource.gallery); onPickFile(result?.path); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_font_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_font_item.dart index 8fddc4edbe..b1004a3eae 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_font_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_font_item.dart @@ -5,7 +5,7 @@ import 'package:appflowy/plugins/document/application/document_appearance_cubit. import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/shared/google_fonts_extension.dart'; -import 'package:appflowy/util/google_font_family_extension.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_cover_image.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_cover_image.dart index 0b72fd60f0..1d6b100534 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_cover_image.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_cover_image.dart @@ -4,13 +4,12 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; -import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_cover_bottom_sheet.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_util.dart'; import 'package:appflowy/shared/feedback_gesture_detector.dart'; -import 'package:appflowy/startup/tasks/device_info_task.dart'; +import 'package:appflowy/shared/permission/permission_checker.dart'; import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; @@ -18,11 +17,9 @@ import 'package:appflowy_result/appflowy_result.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/snap_bar.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:permission_handler/permission_handler.dart'; class PageStyleCoverImage extends StatelessWidget { PageStyleCoverImage({ @@ -121,7 +118,8 @@ class PageStyleCoverImage extends StatelessWidget { } Future _pickImage(BuildContext context) async { - final photoPermission = await _checkPhotoPermission(context); + final photoPermission = + await PermissionChecker.checkPhotoPermission(context); if (!photoPermission) { Log.error('Has no permission to access the photo library'); return; @@ -129,9 +127,7 @@ class PageStyleCoverImage extends StatelessWidget { XFile? result; try { - result = await _imagePicker.pickImage( - source: ImageSource.gallery, - ); + result = await _imagePicker.pickImage(source: ImageSource.gallery); } catch (e) { Log.error('Error while picking image: $e'); return; @@ -224,54 +220,6 @@ class PageStyleCoverImage extends StatelessWidget { }, ); } - - Future _checkPhotoPermission(BuildContext context) async { - // check the permission first - final status = await Permission.photos.status; - // if the permission is permanently denied, we should open the app settings - if (status.isPermanentlyDenied && context.mounted) { - unawaited( - showFlowyMobileConfirmDialog( - context, - title: FlowyText.semibold( - LocaleKeys.pageStyle_photoPermissionTitle.tr(), - maxLines: 3, - textAlign: TextAlign.center, - ), - content: FlowyText( - LocaleKeys.pageStyle_photoPermissionDescription.tr(), - maxLines: 5, - textAlign: TextAlign.center, - fontSize: 12.0, - ), - actionAlignment: ConfirmDialogActionAlignment.vertical, - actionButtonTitle: LocaleKeys.pageStyle_openSettings.tr(), - actionButtonColor: Colors.blue, - cancelButtonTitle: LocaleKeys.pageStyle_doNotAllow.tr(), - cancelButtonColor: Colors.blue, - onActionButtonPressed: () { - openAppSettings(); - }, - ), - ); - - return false; - } else if (status.isDenied) { - // https://github.com/Baseflow/flutter-permission-handler/issues/1262#issuecomment-2006340937 - Permission permission = Permission.photos; - if (defaultTargetPlatform == TargetPlatform.android && - ApplicationInfo.androidSDKVersion <= 32) { - permission = Permission.storage; - } - // if the permission is denied, we should request the permission - final newStatus = await permission.request(); - if (newStatus.isDenied) { - return false; - } - } - - return true; - } } class _UnsplashCover extends StatelessWidget { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_layout.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_layout.dart index c5cebd7ed3..bbe0fda27c 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_layout.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_layout.dart @@ -5,6 +5,7 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_she import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_util.dart'; import 'package:appflowy/shared/feedback_gesture_detector.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -163,11 +164,8 @@ class _FontButton extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - String fontFamily = state.fontFamily ?? builtInFontFamily(); - if (fontFamily == builtInFontFamily()) { - fontFamily = - LocaleKeys.settings_appearance_fontFamily_defaultFont.tr(); - } + final fontFamilyDisplayName = + (state.fontFamily ?? defaultFontFamily).fontFamilyDisplayName; return GestureDetector( onTap: () => _showFontSelector(context), behavior: HitTestBehavior.opaque, @@ -182,7 +180,7 @@ class _FontButton extends StatelessWidget { const HSpace(16.0), FlowyText(LocaleKeys.titleBar_font.tr()), const Spacer(), - FlowyText(fontFamily), + FlowyText(fontFamilyDisplayName), const HSpace(6.0), const FlowySvg(FlowySvgs.m_page_style_arrow_right_s), const HSpace(12.0), @@ -219,7 +217,7 @@ class _FontButton extends StatelessWidget { child: FontSelector( scrollController: controller, selectedFontFamilyName: - state.fontFamily ?? builtInFontFamily(), + state.fontFamily ?? defaultFontFamily, onFontFamilySelected: (fontFamilyName) { context.read().add( DocumentPageStyleEvent.updateFontFamily( 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 02eee6213d..cdf2dcfffd 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -8,7 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_too import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/shared/google_fonts_extension.dart'; -import 'package:appflowy/util/google_font_family_extension.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; @@ -92,7 +92,7 @@ class EditorStyleCustomizer { final theme = Theme.of(context); final fontSize = pageStyle.fontLayout.fontSize; final lineHeight = pageStyle.lineHeightLayout.lineHeight; - final fontFamily = pageStyle.fontFamily ?? builtInFontFamily(); + final fontFamily = pageStyle.fontFamily ?? defaultFontFamily; final defaultTextDirection = context.read().state.defaultTextDirection; final baseTextStyle = this.baseTextStyle(fontFamily); @@ -178,7 +178,7 @@ class EditorStyleCustomizer { TextStyle outlineBlockPlaceholderStyleBuilder() { final fontSize = context.read().state.fontSize; return TextStyle( - fontFamily: builtInFontFamily(), + fontFamily: defaultFontFamily, fontSize: fontSize, height: 1.5, color: Theme.of(context).colorScheme.onBackground.withOpacity(0.6), @@ -219,7 +219,8 @@ class EditorStyleCustomizer { try { return getGoogleFontSafely(fontFamily, fontWeight: fontWeight); } on Exception { - if ([builtInFontFamily(), builtInCodeFontFamily].contains(fontFamily)) { + if ([defaultFontFamily, fallbackFontFamily, builtInCodeFontFamily] + .contains(fontFamily)) { return TextStyle(fontFamily: fontFamily, fontWeight: fontWeight); } diff --git a/frontend/appflowy_flutter/lib/shared/permission/permission_checker.dart b/frontend/appflowy_flutter/lib/shared/permission/permission_checker.dart new file mode 100644 index 0000000000..7ae91fc73e --- /dev/null +++ b/frontend/appflowy_flutter/lib/shared/permission/permission_checker.dart @@ -0,0 +1,64 @@ +// Check if the user has the required permission to access the device's +// - camera +// - storage +// - ... +import 'dart:async'; + +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart'; +import 'package:appflowy/startup/tasks/device_info_task.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class PermissionChecker { + static Future checkPhotoPermission(BuildContext context) async { + // check the permission first + final status = await Permission.photos.status; + // if the permission is permanently denied, we should open the app settings + if (status.isPermanentlyDenied && context.mounted) { + unawaited( + showFlowyMobileConfirmDialog( + context, + title: FlowyText.semibold( + LocaleKeys.pageStyle_photoPermissionTitle.tr(), + maxLines: 3, + textAlign: TextAlign.center, + ), + content: FlowyText( + LocaleKeys.pageStyle_photoPermissionDescription.tr(), + maxLines: 5, + textAlign: TextAlign.center, + fontSize: 12.0, + ), + actionAlignment: ConfirmDialogActionAlignment.vertical, + actionButtonTitle: LocaleKeys.pageStyle_openSettings.tr(), + actionButtonColor: Colors.blue, + cancelButtonTitle: LocaleKeys.pageStyle_doNotAllow.tr(), + cancelButtonColor: Colors.blue, + onActionButtonPressed: () { + openAppSettings(); + }, + ), + ); + + return false; + } else if (status.isDenied) { + // https://github.com/Baseflow/flutter-permission-handler/issues/1262#issuecomment-2006340937 + Permission permission = Permission.photos; + if (defaultTargetPlatform == TargetPlatform.android && + ApplicationInfo.androidSDKVersion <= 32) { + permission = Permission.storage; + } + // if the permission is denied, we should request the permission + final newStatus = await permission.request(); + if (newStatus.isDenied) { + return false; + } + } + + return true; + } +} diff --git a/frontend/appflowy_flutter/lib/util/font_family_extension.dart b/frontend/appflowy_flutter/lib/util/font_family_extension.dart new file mode 100644 index 0000000000..12fb5aaad0 --- /dev/null +++ b/frontend/appflowy_flutter/lib/util/font_family_extension.dart @@ -0,0 +1,15 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; +import 'package:easy_localization/easy_localization.dart'; + +extension FontFamilyExtension on String { + String parseFontFamilyName() => replaceAll('_regular', '') + .replaceAllMapped(camelCaseRegex, (m) => ' ${m.group(0)}'); + + // display the default font name if the font family name is empty + // or using the default font family + String get fontFamilyDisplayName => isEmpty || this == defaultFontFamily + ? LocaleKeys.settings_appearance_fontFamily_defaultFont.tr() + : parseFontFamilyName(); +} diff --git a/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart b/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart deleted file mode 100644 index 30a3229085..0000000000 --- a/frontend/appflowy_flutter/lib/util/google_font_family_extension.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:appflowy/shared/patterns/common_patterns.dart'; - -extension GoogleFontsParser on String { - String parseFontFamilyName() => replaceAll('_regular', '') - .replaceAllMapped(camelCaseRegex, (m) => ' ${m.group(0)}'); -} diff --git a/frontend/appflowy_flutter/lib/workspace/application/appearance_defaults.dart b/frontend/appflowy_flutter/lib/workspace/application/appearance_defaults.dart index 5faed08e79..b3cb390e8e 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/appearance_defaults.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/appearance_defaults.dart @@ -1,9 +1,10 @@ +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; /// A class for the default appearance settings for the app class DefaultAppearanceSettings { - static const kDefaultFontFamily = 'Poppins'; + static const kDefaultFontFamily = defaultFontFamily; static const kDefaultThemeMode = ThemeMode.system; static const kDefaultThemeName = "Default"; static const kDefaultTheme = BuiltInTheme.defaultTheme; 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 6fe42e6273..f2c6141407 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 @@ -1,28 +1,19 @@ -import 'dart:io'; - import 'package:appflowy/shared/google_fonts_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; -String builtInFontFamily() { - if (PlatformExtension.isDesktopOrWeb) { - return 'Poppins'; - } +// the default font family is empty, so we can use the default font family of the platform +// the system will choose the default font family of the platform +// iOS: San Francisco +// Android: Roboto +// Desktop: Based on the OS +const defaultFontFamily = ''; - if (Platform.isIOS) { - return 'San Francisco'; - } - - if (Platform.isAndroid) { - return 'Roboto'; - } - - return 'Roboto'; -} - -// 'Poppins'; +// the Poppins font is embedded in the app, so we can use it without GoogleFonts +// TODO(Lucas): after releasing version 0.5.6, remove it. +const fallbackFontFamily = 'Poppins'; const builtInCodeFontFamily = 'RobotoMono'; abstract class BaseAppearance { @@ -48,17 +39,15 @@ abstract class BaseAppearance { letterSpacing = fontSize * (letterSpacing ?? 0.005); final textStyle = TextStyle( - fontFamily: fontFamily, + fontFamily: fontFamily.isEmpty ? null : fontFamily, fontSize: fontSize, color: fontColor, fontWeight: fontWeight, - fontFamilyFallback: [builtInFontFamily()], letterSpacing: letterSpacing, height: lineHeight, ); - // we embed Poppins font in the app, so we can use it without GoogleFonts - if (fontFamily == builtInFontFamily()) { + if (fontFamily == defaultFontFamily || fontFamily == fallbackFontFamily) { return textStyle; } diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart index 8925fc9bda..001de8af4e 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart @@ -1,9 +1,8 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra/theme_extension.dart'; +import 'package:flutter/material.dart'; class DesktopAppearance extends BaseAppearance { @override @@ -13,7 +12,6 @@ class DesktopAppearance extends BaseAppearance { String fontFamily, String codeFontFamily, ) { - assert(fontFamily.isNotEmpty); assert(codeFontFamily.isNotEmpty); final theme = brightness == Brightness.light diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart index 769071d55d..48a761da75 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart @@ -1,17 +1,17 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; import 'package:appflowy/shared/google_fonts_extension.dart'; -import 'package:appflowy/util/google_font_family_extension.dart'; +import 'package:appflowy/util/font_family_extension.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; +import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -83,7 +83,10 @@ class FontFamilyDropDown extends StatefulWidget { } class _FontFamilyDropDownState extends State { - final List availableFonts = GoogleFonts.asMap().keys.toList(); + final List availableFonts = [ + defaultFontFamily, + ...GoogleFonts.asMap().keys, + ]; final ValueNotifier query = ValueNotifier(''); @override @@ -94,10 +97,11 @@ class _FontFamilyDropDownState extends State { @override Widget build(BuildContext context) { + final currentValue = widget.currentFontFamily.fontFamilyDisplayName; return FlowySettingValueDropDown( popoverKey: ThemeFontFamilySetting.popoverKey, popoverController: widget.popoverController, - currentValue: widget.currentFontFamily.parseFontFamilyName(), + currentValue: currentValue, onClose: () { query.value = ''; widget.onClose?.call(); @@ -168,8 +172,8 @@ class _FontFamilyDropDownState extends State { BuildContext context, TextStyle style, ) { - final buttonFontFamily = style.fontFamily!.parseFontFamilyName(); - + final buttonFontFamily = + style.fontFamily?.parseFontFamilyName() ?? defaultFontFamily; return Tooltip( message: buttonFontFamily, waitDuration: const Duration(milliseconds: 150), @@ -179,8 +183,8 @@ class _FontFamilyDropDownState extends State { child: FlowyButton( onHover: (_) => FocusScope.of(context).unfocus(), text: FlowyText.medium( - buttonFontFamily, - fontFamily: style.fontFamily!, + buttonFontFamily.fontFamilyDisplayName, + fontFamily: buttonFontFamily, ), rightIcon: buttonFontFamily == widget.currentFontFamily.parseFontFamilyName() @@ -190,15 +194,14 @@ class _FontFamilyDropDownState extends State { if (widget.onFontFamilyChanged != null) { widget.onFontFamilyChanged!(buttonFontFamily); } else { - final fontFamily = style.fontFamily!.parseFontFamilyName(); if (widget.currentFontFamily.parseFontFamilyName() != buttonFontFamily) { context .read() - .setFontFamily(fontFamily); + .setFontFamily(buttonFontFamily); context .read() - .syncFontFamily(fontFamily); + .syncFontFamily(buttonFontFamily); } } PopoverContainer.of(context).close(); 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 d3faf00336..4f8f4b786a 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 @@ -36,7 +36,7 @@ void main() { AppTheme.fallback, ), verify: (bloc) { - expect(bloc.state.font, builtInFontFamily()); + expect(bloc.state.font, defaultFontFamily); 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 d2c7102b0d..2f369cd0cf 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 @@ -27,7 +27,7 @@ void main() { test('Initial state', () { expect(cubit.state.fontSize, 16.0); - expect(cubit.state.fontFamily, builtInFontFamily()); + expect(cubit.state.fontFamily, defaultFontFamily); }); test('Fetch document appearance from SharedPreferences', () async { 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 26fac55ecf..d8639aa600 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,7 +1,9 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/application/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:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -56,9 +58,9 @@ void main() { value: documentAppearanceCubit, ), ], - child: Scaffold( + child: const Scaffold( body: ThemeFontFamilySetting( - currentFontFamily: builtInFontFamily(), + currentFontFamily: defaultFontFamily, ), ), ), @@ -71,7 +73,10 @@ void main() { await tester.pumpAndSettle(); // Verify the initial font family - expect(find.text(builtInFontFamily()), findsAtLeastNWidgets(1)); + expect( + find.text(LocaleKeys.settings_appearance_fontFamily_defaultFont.tr()), + findsAtLeastNWidgets(1), + ); when(() => appearanceSettingsCubit.setFontFamily(any())) .thenAnswer((_) async {}); verifyNever(() => appearanceSettingsCubit.setFontFamily(any())); diff --git a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs index 6aa421bdbc..1a4e8f288c 100644 --- a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs +++ b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs @@ -128,7 +128,7 @@ pub struct DocumentSettingsPB { } pub const APPEARANCE_DEFAULT_THEME: &str = "Default"; -pub const APPEARANCE_DEFAULT_FONT: &str = "Poppins"; +pub const APPEARANCE_DEFAULT_FONT: &str = ""; // Use system default font pub const APPEARANCE_DEFAULT_MONOSPACE_FONT: &str = "SF Mono"; const APPEARANCE_RESET_AS_DEFAULT: bool = true; const APPEARANCE_DEFAULT_IS_MENU_COLLAPSED: bool = false;