diff --git a/frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart b/frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart index b2a8d2a69047..b656fc116745 100644 --- a/frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart +++ b/frontend/appflowy_flutter/integration_test/document/document_with_cover_image_test.dart @@ -22,7 +22,6 @@ void main() { // Hover over cover toolbar to show 'Add Cover' and 'Add Icon' buttons await tester.editor.hoverOnCoverToolbar(); - tester.expectToSeePluginAddCoverAndIconButton(); // Insert a document cover await tester.editor.tapOnAddCover(); @@ -58,14 +57,10 @@ void main() { await tester.initializeAppFlowy(); await tester.tapGoButton(); - tester.expectToSeeDocumentIcon(null); - - // Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons - await tester.editor.hoverOnCoverToolbar(); - tester.expectToSeePluginAddCoverAndIconButton(); + tester.expectToSeeDocumentIcon('⭐️'); // Insert a document icon - await tester.editor.tapAddIconButton(); + await tester.editor.tapGettingStartedIcon(); await tester.tapEmoji('😀'); tester.expectToSeeDocumentIcon('😀'); @@ -95,18 +90,15 @@ void main() { await tester.initializeAppFlowy(); await tester.tapGoButton(); - tester.expectToSeeDocumentIcon(null); + tester.expectToSeeDocumentIcon('⭐️'); tester.expectToSeeNoDocumentCover(); - // Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons - await tester.editor.hoverOnCoverToolbar(); - tester.expectToSeePluginAddCoverAndIconButton(); - // Insert a document icon - await tester.editor.tapAddIconButton(); + await tester.editor.tapGettingStartedIcon(); await tester.tapEmoji('😀'); // Insert a document cover + await tester.editor.hoverOnCoverToolbar(); await tester.editor.tapOnAddCover(); // Expect to see the icon and cover at the same time @@ -122,8 +114,7 @@ void main() { await tester.initializeAppFlowy(); await tester.tapGoButton(); - await tester.editor.hoverOnCoverToolbar(); - await tester.editor.tapAddIconButton(); + await tester.editor.tapGettingStartedIcon(); // click the shuffle button await tester.tapButton( @@ -136,8 +127,7 @@ void main() { await tester.initializeAppFlowy(); await tester.tapGoButton(); - await tester.editor.hoverOnCoverToolbar(); - await tester.editor.tapAddIconButton(); + await tester.editor.tapGettingStartedIcon(); final searchEmojiTextField = find.byWidgetPredicate( (widget) => diff --git a/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart b/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart index 7236a4b94bbc..7f089d518a7d 100644 --- a/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart +++ b/frontend/appflowy_flutter/integration_test/util/editor_test_operations.dart @@ -60,6 +60,15 @@ class EditorOperations { expect(find.byType(FlowyEmojiPicker), findsOneWidget); } + Future tapGettingStartedIcon() async { + await tester.tapButton( + find.descendant( + of: find.byType(DocumentHeaderNodeWidget), + matching: find.findTextInFlowyText('⭐️'), + ), + ); + } + /// Taps on the 'Skin tone' button /// /// Must call [tapAddIconButton] first. @@ -67,7 +76,7 @@ class EditorOperations { await tester.tapButton( find.byTooltip(LocaleKeys.emoji_selectSkinTone.tr()), ); - final skinToneButton = find.text(EmojiSkinToneWrapper(skinTone).name); + final skinToneButton = find.byKey(emojiSkinToneKey(skinTone.icon)); await tester.tapButton(skinToneButton); } diff --git a/frontend/appflowy_flutter/integration_test/util/expectation.dart b/frontend/appflowy_flutter/integration_test/util/expectation.dart index 582cd85ae3de..b808ec7b841a 100644 --- a/frontend/appflowy_flutter/integration_test/util/expectation.dart +++ b/frontend/appflowy_flutter/integration_test/util/expectation.dart @@ -12,7 +12,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter_test/flutter_test.dart'; // const String readme = 'Read me'; -const String gettingStarted = '⭐️ Getting started'; +const String gettingStarted = 'Getting started'; extension Expectation on WidgetTester { /// Expect to see the home page and with a default read me page. diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart index 2bc843cc0a44..b4aa9deee101 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/bottom_sheet_action_widget.dart @@ -24,7 +24,6 @@ class BottomSheetActionWidget extends StatelessWidget { icon: FlowySvg( svg, size: const Size.square(22.0), - blendMode: BlendMode.dst, color: iconColor, ), label: Text(text), diff --git a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_skin_tone.dart b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_skin_tone.dart index eebc73ed0536..a3f934ac898a 100644 --- a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_skin_tone.dart +++ b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_skin_tone.dart @@ -1,5 +1,4 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:emoji_mart/emoji_mart.dart'; @@ -10,6 +9,11 @@ import 'package:flutter/material.dart'; // use a temporary global value to store last selected skin tone EmojiSkinTone? lastSelectedEmojiSkinTone; +@visibleForTesting +ValueKey emojiSkinToneKey(String icon) { + return ValueKey('emoji_skin_tone_$icon'); +} + class FlowyEmojiSkinToneSelector extends StatefulWidget { const FlowyEmojiSkinToneSelector({ super.key, @@ -26,73 +30,59 @@ class FlowyEmojiSkinToneSelector extends StatefulWidget { class _FlowyEmojiSkinToneSelectorState extends State { EmojiSkinTone skinTone = EmojiSkinTone.none; + final controller = PopoverController(); @override Widget build(BuildContext context) { - return PopoverActionList( + return AppFlowyPopover( direction: PopoverDirection.bottomWithCenterAligned, - offset: const Offset(0, 8), - actions: EmojiSkinTone.values - .map((action) => EmojiSkinToneWrapper(action)) - .toList(), - buildChild: (controller) { - return FlowyTooltip( - message: LocaleKeys.emoji_selectSkinTone.tr(), - child: FlowyIconButton( - icon: Padding( - // add a left padding to align the emoji center - padding: const EdgeInsets.only( - left: 3.0, - ), - child: FlowyText( - lastSelectedEmojiSkinTone?.icon ?? '✋', - fontSize: 22.0, - ), - ), - onPressed: () => controller.show(), - ), + controller: controller, + popupBuilder: (context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: EmojiSkinTone.values + .map( + (e) => _buildIconButton( + e.icon, + () { + setState(() => lastSelectedEmojiSkinTone = e); + widget.onEmojiSkinToneChanged(e); + controller.close(); + }, + ), + ) + .toList(), ); }, - onSelected: (action, controller) async { - widget.onEmojiSkinToneChanged(action.inner); - setState(() { - lastSelectedEmojiSkinTone = action.inner; - }); - controller.close(); - }, + child: FlowyTooltip( + message: LocaleKeys.emoji_selectSkinTone.tr(), + child: _buildIconButton( + lastSelectedEmojiSkinTone?.icon ?? '✋', + () => controller.show(), + ), + ), ); } -} -class EmojiSkinToneWrapper extends ActionCell { - EmojiSkinToneWrapper(this.inner); - - final EmojiSkinTone inner; - - Widget? icon(Color iconColor) => null; - - @override - String get name { - final String i18n; - switch (inner) { - case EmojiSkinTone.none: - i18n = LocaleKeys.emoji_skinTone_default.tr(); - case EmojiSkinTone.light: - i18n = LocaleKeys.emoji_skinTone_light.tr(); - case EmojiSkinTone.mediumLight: - i18n = LocaleKeys.emoji_skinTone_mediumLight.tr(); - case EmojiSkinTone.medium: - i18n = LocaleKeys.emoji_skinTone_medium.tr(); - case EmojiSkinTone.mediumDark: - i18n = LocaleKeys.emoji_skinTone_mediumDark.tr(); - case EmojiSkinTone.dark: - i18n = LocaleKeys.emoji_skinTone_dark.tr(); - } - return '${inner.icon} $i18n'; + Widget _buildIconButton(String icon, VoidCallback onPressed) { + return FlowyIconButton( + key: emojiSkinToneKey(icon), + icon: Padding( + // add a left padding to align the emoji center + padding: const EdgeInsets.only( + left: 3.0, + ), + child: FlowyText( + icon, + fontSize: 22.0, + ), + ), + onPressed: onPressed, + ); } } -extension on EmojiSkinTone { +extension EmojiSkinToneIcon on EmojiSkinTone { String get icon { switch (this) { case EmojiSkinTone.none: diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_document.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_document.dart index 795c6c6818ae..6a529a3976d3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_document.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_document.dart @@ -123,7 +123,7 @@ class _RowEditorState extends State { scrollController: widget.scrollController, styleCustomizer: EditorStyleCustomizer( context: context, - padding: const EdgeInsets.symmetric(horizontal: 10), + padding: const EdgeInsets.only(left: 16, right: 54), ), showParagraphPlaceholder: (editorState, node) => editorState.document.isEmpty, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/openai/widgets/auto_completion_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/openai/widgets/auto_completion_node_widget.dart index 2541e948152f..121cb0776d91 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/openai/widgets/auto_completion_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/openai/widgets/auto_completion_node_widget.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/build_context_extension.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/text_robot.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart'; @@ -7,6 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/wid import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:easy_localization/easy_localization.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'; @@ -15,8 +17,6 @@ import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:provider/provider.dart'; class AutoCompletionBlockKeys { @@ -169,9 +169,12 @@ class _AutoCompletionBlockComponentState return FlowyTextField( hintText: LocaleKeys.document_plugins_autoGeneratorHintText.tr(), controller: controller, - maxLines: 3, + maxLines: 5, focusNode: textFieldFocusNode, autoFocus: false, + hintTextConstraints: const BoxConstraints( + maxHeight: double.infinity, + ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcut_event.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcut_event.dart index 8e9ad2305b5b..6428e0dccf32 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcut_event.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcut_event.dart @@ -70,9 +70,12 @@ CharacterShortcutEvent insertChildNodeInsideToggleList = CharacterShortcutEvent( // insert a toggle list block below the current toggle list block transaction ..deleteText(node, selection.startIndex, slicedDelta.length) - ..insertNode( + ..insertNodes( selection.start.path.next, - toggleListBlockNode(collapsed: true, delta: slicedDelta), + [ + toggleListBlockNode(collapsed: true, delta: slicedDelta), + paragraphNode(), + ], ) ..afterSelection = Selection.collapsed( Position(path: selection.start.path.next, offset: 0), diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart index 4b8a15590148..35a7fe310b2a 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart @@ -106,16 +106,20 @@ extension ViewExtension on ViewPB { FlowySvgData get iconData => layout.icon; - Future> getAncestors({bool includeSelf = false}) async { + Future> getAncestors({ + bool includeSelf = false, + bool includeRoot = false, + }) async { final ancestors = []; if (includeSelf) { - ancestors.add(this); + final self = await ViewBackendService.getView(id); + ancestors.add(self.getLeftOrNull() ?? this); } var parent = await ViewBackendService.getView(parentViewId); while (parent.isLeft()) { // parent is not null final view = parent.getLeftOrNull(); - if (view == null) { + if (view == null || (!includeRoot && view.parentViewId.isEmpty)) { break; } ancestors.add(view); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart index 74590d790dcf..bdc926ba5223 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart @@ -18,6 +18,12 @@ class SidebarFolder extends StatelessWidget { @override Widget build(BuildContext context) { + // check if there is any duplicate views + final views = this.views.toSet().toList(); + final favoriteViews = this.favoriteViews.toSet().toList(); + assert(views.length == this.views.length); + assert(favoriteViews.length == favoriteViews.length); + return ValueListenableBuilder( valueListenable: getIt().notifier, builder: (context, value, child) { @@ -27,6 +33,7 @@ class SidebarFolder extends StatelessWidget { // favorite if (favoriteViews.isNotEmpty) ...[ FavoriteFolder( + // remove the duplicate views views: favoriteViews, ), const VSpace(10), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/toast.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/toast.dart index 3cb102db4249..a4bcf376317c 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/toast.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/toast.dart @@ -55,6 +55,7 @@ void showSnackBarMessage( }) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( + backgroundColor: Theme.of(context).colorScheme.onSecondary, action: !showCancel ? null : SnackBarAction( @@ -66,7 +67,6 @@ void showSnackBarMessage( ), content: FlowyText( message, - color: Theme.of(context).colorScheme.onSurface, ), ), ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart index d7a1a266572b..6b49630600e7 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart @@ -44,8 +44,8 @@ void showEmojiPickerMenu( builder: (context) => Material( type: MaterialType.transparency, child: Container( - width: 300, - height: 250, + width: 360, + height: 380, padding: const EdgeInsets.all(4.0), decoration: FlowyDecoration.decoration( Theme.of(context).cardColor, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart index 4f36c5cb4c3b..7d07f490f892 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart'; +import 'package:appflowy/startup/tasks/app_window_size_manager.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart'; @@ -29,9 +30,7 @@ class _ViewTitleBarState extends State { void initState() { super.initState(); - ancestors = widget.view.getAncestors( - includeSelf: true, - ); + _reloadAncestors(); } @override @@ -39,9 +38,7 @@ class _ViewTitleBarState extends State { super.didUpdateWidget(oldWidget); if (oldWidget.view.id != widget.view.id) { - ancestors = widget.view.getAncestors( - includeSelf: true, - ); + _reloadAncestors(); } } @@ -51,27 +48,57 @@ class _ViewTitleBarState extends State { future: ancestors, builder: ((context, snapshot) { final ancestors = snapshot.data; - if (ancestors == null || - snapshot.connectionState != ConnectionState.done) { + if (ancestors == null) { return const SizedBox.shrink(); } - return Row( + const maxWidth = WindowSizeManager.minWindowWidth - 100; + final replacement = Row( + // refresh the view title bar when the ancestors changed + key: ValueKey(ancestors.hashCode), children: _buildViewTitles(ancestors), ); + return LayoutBuilder( + builder: (context, constraints) { + return Visibility( + visible: constraints.maxWidth < maxWidth, + replacement: replacement, + // if the width is too small, only show one view title bar without the ancestors + child: _ViewTitle( + view: ancestors.last, + behavior: _ViewTitleBehavior.editable, + maxTitleWidth: constraints.maxWidth - 50.0, + onUpdated: () => setState(() => _reloadAncestors()), + ), + ); + }, + ); }), ); } List _buildViewTitles(List views) { + // if the level is too deep, only show the last two view, the first one view and the root view + bool hasAddedEllipsis = false; final children = []; + for (var i = 0; i < views.length; i++) { final view = views[i]; + if (i >= 1 && i < views.length - 2) { + if (!hasAddedEllipsis) { + hasAddedEllipsis = true; + children.add( + const FlowyText.regular(' ... /'), + ); + } + continue; + } children.add( _ViewTitle( view: view, behavior: i == views.length - 1 ? _ViewTitleBehavior.editable // only the last one is editable : _ViewTitleBehavior.uneditable, // others are not editable + onUpdated: () => setState(() => _reloadAncestors()), ), ); if (i != views.length - 1) { @@ -81,6 +108,12 @@ class _ViewTitleBarState extends State { } return children; } + + void _reloadAncestors() { + ancestors = widget.view.getAncestors( + includeSelf: true, + ); + } } enum _ViewTitleBehavior { @@ -92,10 +125,14 @@ class _ViewTitle extends StatefulWidget { const _ViewTitle({ required this.view, this.behavior = _ViewTitleBehavior.editable, + this.maxTitleWidth = 180, + required this.onUpdated, }); final ViewPB view; final _ViewTitleBehavior behavior; + final double maxTitleWidth; + final VoidCallback onUpdated; @override State<_ViewTitle> createState() => _ViewTitleState(); @@ -124,6 +161,7 @@ class _ViewTitleState extends State<_ViewTitle> { icon = view.icon.value; _resetTextEditingController(); }); + widget.onUpdated(); }, ); } @@ -156,7 +194,15 @@ class _ViewTitleState extends State<_ViewTitle> { fontSize: 18.0, ), const HSpace(2.0), - FlowyText.regular(name), + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: widget.maxTitleWidth, + ), + child: FlowyText.regular( + name, + overflow: TextOverflow.ellipsis, + ), + ), ], ); @@ -188,8 +234,8 @@ class _ViewTitleState extends State<_ViewTitle> { defaultIcon: widget.view.defaultIcon(), direction: PopoverDirection.bottomWithCenterAligned, offset: const Offset(0, 18), - onSubmitted: (emoji, _) { - ViewBackendService.updateViewIcon( + onSubmitted: (emoji, _) async { + await ViewBackendService.updateViewIcon( viewId: widget.view.id, viewIcon: emoji, ); @@ -203,9 +249,9 @@ class _ViewTitleState extends State<_ViewTitle> { child: FlowyTextField( autoFocus: true, controller: textEditingController, - onSubmitted: (text) { + onSubmitted: (text) async { if (text.isNotEmpty && text != name) { - ViewBackendService.updateView( + await ViewBackendService.updateView( viewId: widget.view.id, name: text, ); diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart index 2765c58a9fa1..0d40af1f8adc 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -26,6 +26,7 @@ class FlowyTextField extends StatefulWidget { final Widget? suffixIcon; final BoxConstraints? prefixIconConstraints; final BoxConstraints? suffixIconConstraints; + final BoxConstraints? hintTextConstraints; const FlowyTextField({ super.key, @@ -50,6 +51,7 @@ class FlowyTextField extends StatefulWidget { this.suffixIcon, this.prefixIconConstraints, this.suffixIconConstraints, + this.hintTextConstraints, }); @override @@ -121,15 +123,22 @@ class FlowyTextFieldState extends State { }, onSubmitted: (text) => _onSubmitted(text), onEditingComplete: widget.onEditingComplete, + minLines: 1, maxLines: widget.maxLines, maxLength: widget.maxLength, maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds, style: widget.textStyle ?? Theme.of(context).textTheme.bodySmall, textAlignVertical: TextAlignVertical.center, + keyboardType: TextInputType.multiline, decoration: InputDecoration( - constraints: BoxConstraints( - maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58), - contentPadding: const EdgeInsets.symmetric(horizontal: 12), + constraints: widget.hintTextConstraints ?? + BoxConstraints( + maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 12, + vertical: widget.maxLines > 1 ? 12 : 0, + ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Theme.of(context).colorScheme.outline, diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index a3907b2e198c..59547aeb4546 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -101,11 +101,14 @@ impl FolderOperationHandler for DocumentFolderOperation { FutureResult::new(async move { let mut write_guard = workspace_view_builder.write().await; - // Create a view named "⭐️ Getting started" with built-in README data. + // Create a view named "Getting started" with an icon ⭐️ and the built-in README data. // Don't modify this code unless you know what you are doing. write_guard .with_view_builder(|view_builder| async { - let view = view_builder.with_name("⭐️ Getting started").build(); + let view = view_builder + .with_name("Getting started") + .with_icon("⭐️") + .build(); // create a empty document let json_str = include_str!("../../assets/read_me.json"); let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap(); diff --git a/frontend/rust-lib/flowy-folder2/src/view_operation.rs b/frontend/rust-lib/flowy-folder2/src/view_operation.rs index 20e43ffa5288..7aae7742d568 100644 --- a/frontend/rust-lib/flowy-folder2/src/view_operation.rs +++ b/frontend/rust-lib/flowy-folder2/src/view_operation.rs @@ -96,6 +96,14 @@ impl ViewBuilder { self } + pub fn with_icon(mut self, icon: &str) -> Self { + self.icon = Some(ViewIcon { + ty: collab_folder::IconType::Emoji, + value: icon.to_string(), + }); + self + } + /// Create a child view for the current view. /// The view created by this builder will be the next level view of the current view. pub async fn with_child_view_builder(mut self, child_view_builder: F) -> Self