Skip to content

Commit

Permalink
fix: icons not supported in callout(AppFlowy-IO#7192) (AppFlowy-IO#7197)
Browse files Browse the repository at this point in the history
  • Loading branch information
asjqkkkk authored Jan 13, 2025
1 parent 8df5d8d commit 0c6d4df
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'dart:convert';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/base/icon/icon_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart';
import 'package:easy_localization/easy_localization.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() {
setUpAll(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
RecentIcons.enable = false;
});

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

testWidgets('callout with emoji icon picker', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
final emojiIconData = await tester.loadIcon();

/// create a new document
await tester.createNewPageWithNameUnderParent();

/// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);

/// create callout
await tester.editor.showSlashMenu();
await tester.pumpAndSettle();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_callout.tr(),
);

/// select an icon
final emojiPickerButton = find.descendant(
of: find.byType(CalloutBlockComponentWidget),
matching: find.byType(EmojiPickerButton),
);
await tester.tapButton(emojiPickerButton);
await tester.tapIcon(emojiIconData);

/// verification results
final iconData = IconsData.fromJson(jsonDecode(emojiIconData.emoji));
final iconWidget = find
.descendant(
of: emojiPickerButton,
matching: find.byType(IconWidget),
)
.evaluate()
.first
.widget as IconWidget;
final iconWidgetData = iconWidget.data;
expect(iconWidgetData.iconContent, iconData.iconContent);
expect(iconWidgetData.iconName, iconData.iconName);
expect(iconWidgetData.groupName, iconData.groupName);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
RecentIcons.getEmojiIds().then((v) {
if (v.isEmpty) {
emojiData = data;
setState(() => loaded = true);
if (mounted) setState(() => loaded = true);
return;
}
final categories = List.of(data.categories);
Expand All @@ -148,7 +148,7 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
),
);
emojiData = EmojiData(categories: categories, emojis: data.emojis);
setState(() => loaded = true);
if (mounted) setState(() => loaded = true);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart' show LocaleKeys;
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart'
show StringTranslateExtension;
Expand Down Expand Up @@ -33,17 +34,22 @@ class CalloutBlockKeys {
///
/// The value is a String.
static const String icon = 'icon';

/// the type of [FlowyIconType]
static const String iconType = 'icon_type';
}

// The one is inserted through selection menu
Node calloutNode({
Delta? delta,
String emoji = '📌',
EmojiIconData? emoji,
Color? defaultColor,
}) {
final defaultEmoji = emoji ?? EmojiIconData.emoji('📌');
final attributes = {
CalloutBlockKeys.delta: (delta ?? Delta()).toJson(),
CalloutBlockKeys.icon: emoji,
CalloutBlockKeys.icon: defaultEmoji.emoji,
CalloutBlockKeys.iconType: defaultEmoji.type,
CalloutBlockKeys.backgroundColor: defaultColor?.toHex(),
};
return Node(
Expand Down Expand Up @@ -156,12 +162,19 @@ class _CalloutBlockComponentWidgetState
}

// get the emoji of the note block from the node's attributes or default to '📌'
String get emoji {
EmojiIconData get emoji {
final icon = node.attributes[CalloutBlockKeys.icon];
if (icon == null || icon.isEmpty) {
return '📌';
final type = node.attributes[CalloutBlockKeys.iconType];
EmojiIconData result = EmojiIconData.emoji('📌');
try {
result = EmojiIconData(FlowyIconType.values.byName(type), icon);
} catch (e) {
Log.error(
'get emoji error with icon:[$icon], type:[$type] within alloutBlockComponentWidget',
e,
);
}
return icon;
return result;
}

// get access to the editor state via provider
Expand Down Expand Up @@ -193,16 +206,16 @@ class _CalloutBlockComponentWidgetState
// the emoji picker button for the note
EmojiPickerButton(
// force to refresh the popover state
key: ValueKey(widget.node.id + emoji),
key: ValueKey(widget.node.id + emoji.emoji),
enable: editorState.editable,
title: '',
emoji: EmojiIconData.emoji(emoji),
emoji: emoji,
emojiSize: emojiSize,
showBorder: false,
buttonSize: emojiButtonSize,
onSubmitted: (emoji, controller) {
setEmoji(emoji.emoji);
controller?.close();
onSubmitted: (r, controller) {
setEmojiIconData(r.data);
if (!r.keepOpen) controller?.close();
},
),
if (UniversalPlatform.isDesktopOrWeb) const HSpace(4.0),
Expand Down Expand Up @@ -270,10 +283,11 @@ class _CalloutBlockComponentWidgetState
}

// set the emoji of the note block
Future<void> setEmoji(String emoji) async {
Future<void> setEmojiIconData(EmojiIconData data) async {
final transaction = editorState.transaction
..updateNode(node, {
CalloutBlockKeys.icon: emoji,
CalloutBlockKeys.icon: data.emoji,
CalloutBlockKeys.iconType: data.type.name,
})
..afterSelection = Selection.collapsed(
Position(path: node.path, offset: node.delta?.length ?? 0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';

import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.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';
Expand Down Expand Up @@ -52,7 +53,9 @@ class EditorMigration {
node = pageNode(children: children);
}
} else if (id == 'callout') {
final emoji = nodeV0.attributes['emoji'] ?? '📌';
final icon = nodeV0.attributes[CalloutBlockKeys.icon] ?? '📌';
final iconType = nodeV0.attributes[CalloutBlockKeys.iconType] ??
FlowyIconType.emoji.name;
final delta =
nodeV0.children.whereType<TextNodeV0>().fold(Delta(), (p, e) {
final delta = migrateDelta(e.delta);
Expand All @@ -62,8 +65,18 @@ class EditorMigration {
}
return p..insert('\n');
});
EmojiIconData? emojiIconData;
try {
emojiIconData =
EmojiIconData(FlowyIconType.values.byName(iconType), icon);
} catch (e) {
Log.error(
'migrateNode get EmojiIconData error with :${nodeV0.attributes}',
e,
);
}
node = calloutNode(
emoji: emoji,
emoji: emojiIconData,
delta: delta,
);
} else if (id == 'divider') {
Expand Down

0 comments on commit 0c6d4df

Please sign in to comment.