Skip to content

Commit

Permalink
fix: icon picker issues on mobile (#7113)
Browse files Browse the repository at this point in the history
* fix: error displaying in Page style

* fix: error displaying in Favorite/Recent page

* fix: complete the filter logic of icon picker

* fix: the color picker showed when tapping down

* fix: icons are not supported in subpage blocks

* chore: add some tests

* fix: recent icons not working for grid header icon
  • Loading branch information
asjqkkkk committed Jan 3, 2025
1 parent d175735 commit b8e3a3f
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:io';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart';
import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand All @@ -11,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import '../../shared/emoji.dart';
import '../../shared/util.dart';

// Test cases for the Document SubPageBlock that needs to be covered:
Expand All @@ -37,7 +40,14 @@ import '../../shared/util.dart';
const _defaultPageName = "";

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
RecentIcons.enable = false;
});

tearDownAll(() {
RecentIcons.enable = true;
});

group('Document SubPageBlock tests', () {
testWidgets('Insert a new SubPageBlock from Slash menu items',
Expand Down Expand Up @@ -498,6 +508,38 @@ void main() {

expect(find.text('Parent'), findsNWidgets(2));
});

testWidgets('Displaying icon of subpage', (tester) async {
const firstPage = 'FirstPage';

await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(name: firstPage);
final icon = await tester.loadIcon();

/// create subpage
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_subPage_name.tr(),
offset: 100,
);

/// add icon
await tester.editor.hoverOnCoverToolbar();
await tester.editor.tapAddIconButton();
await tester.tapIcon(icon);
await tester.pumpAndSettle();
await tester.openPage(firstPage);

/// check if there is a icon in document
final iconWidget = find.byWidgetPredicate((w) {
if (w is! RawEmojiIconWidget) return false;
final iconData = w.emoji.emoji;
return iconData == icon.emoji;
});
expect(iconWidget, findsOneWidget);
});
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart';
Expand All @@ -27,21 +25,6 @@ void main() {
RecentIcons.enable = true;
});

Future<EmojiIconData> loadIcon() async {
await loadIconGroups();
final groups = kIconGroups!;
final firstGroup = groups.first;
final firstIcon = firstGroup.icons.first;
return EmojiIconData.icon(
IconsData(
firstGroup.name,
firstIcon.content,
firstIcon.name,
builtInSpaceColors.first,
),
);
}

testWidgets('Update page emoji in sidebar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
Expand Down Expand Up @@ -160,7 +143,7 @@ void main() {
testWidgets('Update page icon in sidebar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
final iconData = await loadIcon();
final iconData = await tester.loadIcon();

// create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) {
Expand Down Expand Up @@ -192,7 +175,7 @@ void main() {
testWidgets('Update page icon in title bar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
final iconData = await loadIcon();
final iconData = await tester.loadIcon();

// create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
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/base/view_page/app_bar_buttons.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_buttons.dart';
import 'package:appflowy/mobile/presentation/mobile_bottom_navigation_bar.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_icon.dart';
import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import '../../shared/emoji.dart';
import '../../shared/util.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
RecentIcons.enable = false;
});

tearDownAll(() {
RecentIcons.enable = true;
});

group('document page style:', () {
double getCurrentEditorFontSize() {
Expand Down Expand Up @@ -114,5 +127,37 @@ void main() {
);
expect(builtInCover, findsOneWidget);
});

testWidgets('page style icon', (tester) async {
await tester.launchInAnonymousMode();

final createPageButton =
find.byKey(BottomNavigationBarItemType.add.valueKey);
await tester.tapButton(createPageButton);

/// toggle the preset button
await tester.tapSvgButton(FlowySvgs.m_layout_s);

/// select document plugins emoji
final pageStyleIcon = find.byType(PageStyleIcon);

/// there should be none of emoji
final noneText = find.text(LocaleKeys.pageStyle_none.tr());
expect(noneText, findsOneWidget);
await tester.tapButton(pageStyleIcon);

/// select an emoji
const emoji = '😄';
await tester.tapEmoji(emoji);
await tester.tapSvgButton(FlowySvgs.m_layout_s);
expect(noneText, findsNothing);
expect(
find.descendant(
of: pageStyleIcon,
matching: find.text(emoji),
),
findsOneWidget,
);
});
});
}
27 changes: 27 additions & 0 deletions frontend/appflowy_flutter/integration_test/shared/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,33 @@ extension AppFlowyTestBase on WidgetTester {
}
}

Future<void> tapDown(
Finder finder, {
int? pointer,
int buttons = kPrimaryButton,
PointerDeviceKind kind = PointerDeviceKind.touch,
bool pumpAndSettle = true,
int milliseconds = 500,
}) async {
final location = getCenter(finder);
final TestGesture gesture = await startGesture(
location,
pointer: pointer,
buttons: buttons,
kind: kind,
);
await gesture.cancel();
await gesture.down(location);
await gesture.cancel();
if (pumpAndSettle) {
await this.pumpAndSettle(
Duration(milliseconds: milliseconds),
EnginePhase.sendSemanticsUpdate,
const Duration(seconds: 15),
);
}
}

Future<void> tapButtonWithName(
String tr, {
int milliseconds = 500,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_tab
import 'package:appflowy/plugins/shared/share/share_button.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/shared/text_field/text_filed_with_metric_lines.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/presentation/screens/screens.dart';
Expand All @@ -23,6 +24,7 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
Expand Down Expand Up @@ -898,6 +900,22 @@ extension CommonOperations on WidgetTester {
await tapAt(Offset.zero);
await pumpUntilNotFound(finder);
}

/// load icon list and return the first one
Future<EmojiIconData> loadIcon() async {
await loadIconGroups();
final groups = kIconGroups!;
final firstGroup = groups.first;
final firstIcon = firstGroup.icons.first;
return EmojiIconData.icon(
IconsData(
firstGroup.name,
firstIcon.content,
firstIcon.name,
builtInSpaceColors.first,
),
);
}
}

extension SettingsFinder on CommonFinders {
Expand Down
5 changes: 5 additions & 0 deletions frontend/appflowy_flutter/integration_test/shared/emoji.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ extension EmojiTestExtension on WidgetTester {
),
);
expect(find.byType(IconColorPicker), findsNothing);

/// test for tapping down, it should not display the ColorPicker unless tapping up
await tapDown(selectedSvg);
expect(find.byType(IconColorPicker), findsNothing);

await tapButton(selectedSvg);
final colorPicker = find.byType(IconColorPicker);
expect(colorPicker, findsOneWidget);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,16 @@ class MobileViewPage extends StatelessWidget {
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
WidgetSpan(
child: SizedBox(
width: 20,
child: EmojiIconWidget(
emoji: icon,
emojiSize: 17.0,
if (icon.isNotEmpty)
WidgetSpan(
child: SizedBox(
width: 20,
child: EmojiIconWidget(
emoji: icon,
emojiSize: 17.0,
),
),
),
),
if (icon.isNotEmpty) const WidgetSpan(child: HSpace(2.0)),
TextSpan(
text: name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ class _PageStyleIconState extends State<PageStyleIcon> {
const HSpace(16.0),
FlowyText(LocaleKeys.document_plugins_emoji.tr()),
const Spacer(),
RawEmojiIconWidget(
emoji: icon.isNotEmpty
? icon
: EmojiIconData.emoji(LocaleKeys.pageStyle_none.tr()),
emojiSize: icon.isNotEmpty ? 22.0 : 16.0,
),
icon.isEmpty
? FlowyText(
LocaleKeys.pageStyle_none.tr(),
fontSize: 16.0,
)
: RawEmojiIconWidget(
emoji: icon,
emojiSize: 22.0,
),
const HSpace(6.0),
const FlowySvg(FlowySvgs.m_page_style_arrow_right_s),
const HSpace(12.0),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/shared_context/shared_context.dart';
import 'package:appflowy/plugins/trash/application/trash_listener.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
Expand All @@ -15,7 +17,6 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_result/appflowy_result.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/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -242,12 +243,9 @@ class SubPageBlockComponentState extends State<SubPageBlockComponent>
children: [
const HSpace(10),
view.icon.value.isNotEmpty
? FlowyText.emoji(
view.icon.value,
fontSize: textStyle.fontSize,
lineHeight: textStyle.height,
color:
AFThemeExtension.of(context).strongText,
? RawEmojiIconWidget(
emoji: view.icon.toEmojiIconData(),
emojiSize: textStyle.fontSize ?? 16.0,
)
: view.defaultIcon(),
const HSpace(6),
Expand Down
25 changes: 23 additions & 2 deletions frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ class IconGroup {
final filteredIcons = icons
.where(
(icon) =>
icon.keywords.any((k) => k.contains(lowercaseKey)) ||
icon.name.contains(lowercaseKey),
icon.keywords
.any((k) => k.toLowerCase().contains(lowercaseKey)) ||
icon.name.toLowerCase().contains(lowercaseKey),
)
.toList();
return IconGroup(name: name, icons: filteredIcons);
Expand Down Expand Up @@ -84,3 +85,23 @@ class Icon {
return '${iconGroup!.name}/$name';
}
}

class RecentIcon {
factory RecentIcon.fromJson(Map<String, dynamic> json) =>
RecentIcon(_$IconFromJson(json), json['groupName'] ?? '');

RecentIcon(this.icon, this.groupName);

final Icon icon;
final String groupName;

String get name => icon.name;

List<String> get keywords => icon.keywords;

String get content => icon.content;

Map<String, dynamic> toJson() => _$IconToJson(
Icon(name: name, keywords: keywords, content: content),
)..addAll({'groupName': groupName});
}
Loading

0 comments on commit b8e3a3f

Please sign in to comment.