mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: workspace settings page (#5225)
* feat: my account settings page * test: amend tests * chore: remove unused code * test: remove widget tests * fix: text color on select buttons * test: clean and remove unused test helpers * feat: settings workspace page * chore: fixes after merge * fix: recent views bugfix * fix: make sure text buttons have color * test: add test for delete workspace in settings * test: remove pumpAndSettle for create workspace * test: longer pump duration * test: attempt with large pump duration * test: attempt workaround * chore: clean code * fix: missing language key * test: add one more check * test: pump * test: more pump * test: attempt pumpAndSettle * chore: code review * fix: persist single workspace on patch * fix: listen to workspace changes * chore: remove redundant builder * test: remove unstable test * fix: changes after merge * chore: changes after merge * feat: support changing cursor and selection color * chore: move members up in menu * feat: clean code and beautify dialogs * fix: fix test and make show selected font --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
@ -1,13 +1,28 @@
|
||||
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||
import 'package:appflowy/util/font_family_extension.dart';
|
||||
import 'package:appflowy/util/levenshtein.dart';
|
||||
import 'package:appflowy/workspace/application/appearance_defaults.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_list_tile.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_value_dropdown.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
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/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
final customizeFontToolbarItem = ToolbarItem(
|
||||
id: 'editor.font',
|
||||
@ -16,10 +31,12 @@ final customizeFontToolbarItem = ToolbarItem(
|
||||
builder: (context, editorState, highlightColor, _) {
|
||||
final selection = editorState.selection!;
|
||||
final popoverController = PopoverController();
|
||||
final String? currentFontFamily = editorState
|
||||
.getDeltaAttributeValueInSelection(AppFlowyRichTextKeys.fontFamily);
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: FontFamilyDropDown(
|
||||
currentFontFamily: '',
|
||||
currentFontFamily: currentFontFamily ?? '',
|
||||
offset: const Offset(0, 12),
|
||||
popoverController: popoverController,
|
||||
onOpen: () => keepEditorFocusNotifier.increase(),
|
||||
@ -35,8 +52,11 @@ final customizeFontToolbarItem = ToolbarItem(
|
||||
Log.error('Failed to set font family: $e');
|
||||
}
|
||||
},
|
||||
onResetFont: () async => editorState
|
||||
.formatDelta(selection, {AppFlowyRichTextKeys.fontFamily: null}),
|
||||
onResetFont: () async {
|
||||
popoverController.close();
|
||||
await editorState
|
||||
.formatDelta(selection, {AppFlowyRichTextKeys.fontFamily: null});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: FlowyTooltip(
|
||||
@ -52,3 +72,227 @@ final customizeFontToolbarItem = ToolbarItem(
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
class ThemeFontFamilySetting extends StatefulWidget {
|
||||
const ThemeFontFamilySetting({
|
||||
super.key,
|
||||
required this.currentFontFamily,
|
||||
});
|
||||
|
||||
final String currentFontFamily;
|
||||
static Key textFieldKey = const Key('FontFamilyTextField');
|
||||
static Key resetButtonkey = const Key('FontFamilyResetButton');
|
||||
static Key popoverKey = const Key('FontFamilyPopover');
|
||||
|
||||
@override
|
||||
State<ThemeFontFamilySetting> createState() => _ThemeFontFamilySettingState();
|
||||
}
|
||||
|
||||
class _ThemeFontFamilySettingState extends State<ThemeFontFamilySetting> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingListTile(
|
||||
label: LocaleKeys.settings_appearance_fontFamily_label.tr(),
|
||||
resetButtonKey: ThemeFontFamilySetting.resetButtonkey,
|
||||
onResetRequested: () {
|
||||
context.read<AppearanceSettingsCubit>().resetFontFamily();
|
||||
context
|
||||
.read<DocumentAppearanceCubit>()
|
||||
.syncFontFamily(DefaultAppearanceSettings.kDefaultFontFamily);
|
||||
},
|
||||
trailing: [
|
||||
FontFamilyDropDown(currentFontFamily: widget.currentFontFamily),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FontFamilyDropDown extends StatefulWidget {
|
||||
const FontFamilyDropDown({
|
||||
super.key,
|
||||
required this.currentFontFamily,
|
||||
this.onOpen,
|
||||
this.onClose,
|
||||
this.onFontFamilyChanged,
|
||||
this.child,
|
||||
this.popoverController,
|
||||
this.offset,
|
||||
this.showResetButton = false,
|
||||
this.onResetFont,
|
||||
});
|
||||
|
||||
final String currentFontFamily;
|
||||
final VoidCallback? onOpen;
|
||||
final VoidCallback? onClose;
|
||||
final void Function(String fontFamily)? onFontFamilyChanged;
|
||||
final Widget? child;
|
||||
final PopoverController? popoverController;
|
||||
final Offset? offset;
|
||||
final bool showResetButton;
|
||||
final VoidCallback? onResetFont;
|
||||
|
||||
@override
|
||||
State<FontFamilyDropDown> createState() => _FontFamilyDropDownState();
|
||||
}
|
||||
|
||||
class _FontFamilyDropDownState extends State<FontFamilyDropDown> {
|
||||
final List<String> availableFonts = [
|
||||
defaultFontFamily,
|
||||
...GoogleFonts.asMap().keys,
|
||||
];
|
||||
final ValueNotifier<String> query = ValueNotifier('');
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
query.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentValue = widget.currentFontFamily.fontFamilyDisplayName;
|
||||
return SettingValueDropDown(
|
||||
popoverKey: ThemeFontFamilySetting.popoverKey,
|
||||
popoverController: widget.popoverController,
|
||||
currentValue: currentValue,
|
||||
onClose: () {
|
||||
query.value = '';
|
||||
widget.onClose?.call();
|
||||
},
|
||||
offset: widget.offset,
|
||||
child: widget.child,
|
||||
popupBuilder: (_) {
|
||||
widget.onOpen?.call();
|
||||
return CustomScrollView(
|
||||
shrinkWrap: true,
|
||||
slivers: [
|
||||
if (widget.showResetButton)
|
||||
SliverPersistentHeader(
|
||||
delegate: _ResetFontButton(onPressed: widget.onResetFont),
|
||||
pinned: true,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: FlowyTextField(
|
||||
key: ThemeFontFamilySetting.textFieldKey,
|
||||
hintText:
|
||||
LocaleKeys.settings_appearance_fontFamily_search.tr(),
|
||||
autoFocus: false,
|
||||
debounceDuration: const Duration(milliseconds: 300),
|
||||
onChanged: (value) {
|
||||
query.value = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 4),
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: query,
|
||||
builder: (context, value, child) {
|
||||
var displayed = availableFonts;
|
||||
if (value.isNotEmpty) {
|
||||
displayed = availableFonts
|
||||
.where(
|
||||
(font) => font
|
||||
.toLowerCase()
|
||||
.contains(value.toLowerCase().toString()),
|
||||
)
|
||||
.sorted((a, b) => levenshtein(a, b))
|
||||
.toList();
|
||||
}
|
||||
return SliverFixedExtentList.builder(
|
||||
itemBuilder: (context, index) => _fontFamilyItemButton(
|
||||
context,
|
||||
getGoogleFontSafely(displayed[index]),
|
||||
),
|
||||
itemCount: displayed.length,
|
||||
itemExtent: 32,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _fontFamilyItemButton(
|
||||
BuildContext context,
|
||||
TextStyle style,
|
||||
) {
|
||||
final buttonFontFamily =
|
||||
style.fontFamily?.parseFontFamilyName() ?? defaultFontFamily;
|
||||
return Tooltip(
|
||||
message: buttonFontFamily,
|
||||
waitDuration: const Duration(milliseconds: 150),
|
||||
child: SizedBox(
|
||||
key: ValueKey(buttonFontFamily),
|
||||
height: 32,
|
||||
child: FlowyButton(
|
||||
onHover: (_) => FocusScope.of(context).unfocus(),
|
||||
text: FlowyText.medium(
|
||||
buttonFontFamily.fontFamilyDisplayName,
|
||||
fontFamily: buttonFontFamily,
|
||||
),
|
||||
rightIcon:
|
||||
buttonFontFamily == widget.currentFontFamily.parseFontFamilyName()
|
||||
? const FlowySvg(FlowySvgs.check_s)
|
||||
: null,
|
||||
onTap: () {
|
||||
if (widget.onFontFamilyChanged != null) {
|
||||
widget.onFontFamilyChanged!(buttonFontFamily);
|
||||
} else {
|
||||
if (widget.currentFontFamily.parseFontFamilyName() !=
|
||||
buttonFontFamily) {
|
||||
context
|
||||
.read<AppearanceSettingsCubit>()
|
||||
.setFontFamily(buttonFontFamily);
|
||||
context
|
||||
.read<DocumentAppearanceCubit>()
|
||||
.syncFontFamily(buttonFontFamily);
|
||||
}
|
||||
}
|
||||
PopoverContainer.of(context).close();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ResetFontButton extends SliverPersistentHeaderDelegate {
|
||||
_ResetFontButton({this.onPressed});
|
||||
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
double shrinkOffset,
|
||||
bool overlapsContent,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8, bottom: 8.0),
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.document_toolbar_resetToDefaultFont.tr(),
|
||||
fontColor: AFThemeExtension.of(context).textColor,
|
||||
fontHoverColor: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: 12,
|
||||
onPressed: onPressed,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => 35;
|
||||
|
||||
@override
|
||||
double get minExtent => 35;
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
||||
true;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||
@ -14,8 +17,6 @@ import 'package:appflowy/workspace/application/settings/appearance/appearance_cu
|
||||
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';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@ -48,9 +49,9 @@ class EditorStyleCustomizer {
|
||||
return EditorStyle.desktop(
|
||||
padding: padding,
|
||||
cursorColor: appearance.cursorColor ??
|
||||
DefaultAppearanceSettings.getDefaultDocumentCursorColor(context),
|
||||
DefaultAppearanceSettings.getDefaultCursorColor(context),
|
||||
selectionColor: appearance.selectionColor ??
|
||||
DefaultAppearanceSettings.getDefaultDocumentSelectionColor(context),
|
||||
DefaultAppearanceSettings.getDefaultSelectionColor(context),
|
||||
defaultTextDirection: appearance.defaultTextDirection,
|
||||
textStyleConfiguration: TextStyleConfiguration(
|
||||
text: baseTextStyle(fontFamily).copyWith(
|
||||
|
Reference in New Issue
Block a user