fix: icon picker issues on mobile (#7113)

* 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
This commit is contained in:
Morn 2025-01-03 10:04:14 +08:00 committed by GitHub
parent f7f99a162e
commit 15deb8ea79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 239 additions and 63 deletions

View File

@ -1,7 +1,9 @@
import 'dart:io'; import 'dart:io';
import 'package:appflowy/generated/locale_keys.g.dart'; 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/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/workspace/presentation/home/menu/view/view_action_type.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
@ -11,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import '../../shared/emoji.dart';
import '../../shared/util.dart'; import '../../shared/util.dart';
// Test cases for the Document SubPageBlock that needs to be covered: // Test cases for the Document SubPageBlock that needs to be covered:
@ -37,7 +40,14 @@ import '../../shared/util.dart';
const _defaultPageName = ""; const _defaultPageName = "";
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
RecentIcons.enable = false;
});
tearDownAll(() {
RecentIcons.enable = true;
});
group('Document SubPageBlock tests', () { group('Document SubPageBlock tests', () {
testWidgets('Insert a new SubPageBlock from Slash menu items', testWidgets('Insert a new SubPageBlock from Slash menu items',
@ -498,6 +508,38 @@ void main() {
expect(find.text('Parent'), findsNWidgets(2)); 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);
});
}); });
} }

View File

@ -1,9 +1,7 @@
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart'; 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/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/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/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/workspace/presentation/widgets/view_title_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart'; import 'package:flowy_infra_ui/style_widget/text_field.dart';
@ -27,21 +25,6 @@ void main() {
RecentIcons.enable = true; 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 { testWidgets('Update page emoji in sidebar', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton(); await tester.tapAnonymousSignInButton();
@ -160,7 +143,7 @@ void main() {
testWidgets('Update page icon in sidebar', (tester) async { testWidgets('Update page icon in sidebar', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton(); await tester.tapAnonymousSignInButton();
final iconData = await loadIcon(); final iconData = await tester.loadIcon();
// create document, board, grid and calendar views // create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) { for (final value in ViewLayoutPB.values) {
@ -192,7 +175,7 @@ void main() {
testWidgets('Update page icon in title bar', (tester) async { testWidgets('Update page icon in title bar', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton(); await tester.tapAnonymousSignInButton();
final iconData = await loadIcon(); final iconData = await tester.loadIcon();
// create document, board, grid and calendar views // create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) { for (final value in ViewLayoutPB.values) {

View File

@ -1,17 +1,30 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; 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/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/base/view_page/app_bar_buttons.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_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_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.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/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import '../../shared/emoji.dart';
import '../../shared/util.dart'; import '../../shared/util.dart';
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
RecentIcons.enable = false;
});
tearDownAll(() {
RecentIcons.enable = true;
});
group('document page style:', () { group('document page style:', () {
double getCurrentEditorFontSize() { double getCurrentEditorFontSize() {
@ -114,5 +127,37 @@ void main() {
); );
expect(builtInCover, findsOneWidget); 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,
);
});
}); });
} }

View File

@ -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( Future<void> tapButtonWithName(
String tr, { String tr, {
int milliseconds = 500, int milliseconds = 500,

View File

@ -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/plugins/shared/share/share_button.dart';
import 'package:appflowy/shared/feature_flags.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/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/shared/text_field/text_filed_with_metric_lines.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/presentation/screens/screens.dart'; import 'package:appflowy/user/presentation/screens/screens.dart';
@ -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/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_header.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_menu.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_menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.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'; import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
@ -898,6 +900,22 @@ extension CommonOperations on WidgetTester {
await tapAt(Offset.zero); await tapAt(Offset.zero);
await pumpUntilNotFound(finder); 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 { extension SettingsFinder on CommonFinders {

View File

@ -42,6 +42,11 @@ extension EmojiTestExtension on WidgetTester {
), ),
); );
expect(find.byType(IconColorPicker), findsNothing); 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); await tapButton(selectedSvg);
final colorPicker = find.byType(IconColorPicker); final colorPicker = find.byType(IconColorPicker);
expect(colorPicker, findsOneWidget); expect(colorPicker, findsOneWidget);

View File

@ -178,15 +178,16 @@ class MobileViewPage extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
text: TextSpan( text: TextSpan(
children: [ children: [
WidgetSpan( if (icon.isNotEmpty)
child: SizedBox( WidgetSpan(
width: 20, child: SizedBox(
child: EmojiIconWidget( width: 20,
emoji: icon, child: EmojiIconWidget(
emojiSize: 17.0, emoji: icon,
emojiSize: 17.0,
),
), ),
), ),
),
if (icon.isNotEmpty) const WidgetSpan(child: HSpace(2.0)), if (icon.isNotEmpty) const WidgetSpan(child: HSpace(2.0)),
TextSpan( TextSpan(
text: name, text: name,

View File

@ -48,12 +48,15 @@ class _PageStyleIconState extends State<PageStyleIcon> {
const HSpace(16.0), const HSpace(16.0),
FlowyText(LocaleKeys.document_plugins_emoji.tr()), FlowyText(LocaleKeys.document_plugins_emoji.tr()),
const Spacer(), const Spacer(),
RawEmojiIconWidget( icon.isEmpty
emoji: icon.isNotEmpty ? FlowyText(
? icon LocaleKeys.pageStyle_none.tr(),
: EmojiIconData.emoji(LocaleKeys.pageStyle_none.tr()), fontSize: 16.0,
emojiSize: icon.isNotEmpty ? 22.0 : 16.0, )
), : RawEmojiIconWidget(
emoji: icon,
emojiSize: 22.0,
),
const HSpace(6.0), const HSpace(6.0),
const FlowySvg(FlowySvgs.m_page_style_arrow_right_s), const FlowySvg(FlowySvgs.m_page_style_arrow_right_s),
const HSpace(12.0), const HSpace(12.0),

View File

@ -1,8 +1,10 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.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/mention/mention_page_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/shared_context/shared_context.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/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/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.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_ext.dart';
@ -15,7 +17,6 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_result/appflowy_result.dart'; import 'package:appflowy_result/appflowy_result.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.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/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -242,12 +243,9 @@ class SubPageBlockComponentState extends State<SubPageBlockComponent>
children: [ children: [
const HSpace(10), const HSpace(10),
view.icon.value.isNotEmpty view.icon.value.isNotEmpty
? FlowyText.emoji( ? RawEmojiIconWidget(
view.icon.value, emoji: view.icon.toEmojiIconData(),
fontSize: textStyle.fontSize, emojiSize: textStyle.fontSize ?? 16.0,
lineHeight: textStyle.height,
color:
AFThemeExtension.of(context).strongText,
) )
: view.defaultIcon(), : view.defaultIcon(),
const HSpace(6), const HSpace(6),

View File

@ -39,8 +39,9 @@ class IconGroup {
final filteredIcons = icons final filteredIcons = icons
.where( .where(
(icon) => (icon) =>
icon.keywords.any((k) => k.contains(lowercaseKey)) || icon.keywords
icon.name.contains(lowercaseKey), .any((k) => k.toLowerCase().contains(lowercaseKey)) ||
icon.name.toLowerCase().contains(lowercaseKey),
) )
.toList(); .toList();
return IconGroup(name: name, icons: filteredIcons); return IconGroup(name: name, icons: filteredIcons);
@ -84,3 +85,23 @@ class Icon {
return '${iconGroup!.name}/$name'; 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});
}

View File

@ -118,10 +118,13 @@ class _FlowyIconPickerState extends State<FlowyIconPicker> {
iconGroups.add( iconGroups.add(
IconGroup( IconGroup(
name: _kRecentIconGroupName, name: _kRecentIconGroupName,
icons: recentIcons.sublist( icons: recentIcons
0, .sublist(
min(recentIcons.length, widget.iconPerLine), 0,
), min(recentIcons.length, widget.iconPerLine),
)
.map((e) => e.icon)
.toList(),
), ),
); );
} }
@ -171,7 +174,7 @@ class _FlowyIconPickerState extends State<FlowyIconPicker> {
color, color,
).toResult(isRandom: true), ).toResult(isRandom: true),
); );
RecentIcons.putIcon(value.$2); RecentIcons.putIcon(RecentIcon(value.$2, value.$1.name));
}, },
onKeywordChanged: (keyword) => { onKeywordChanged: (keyword) => {
debounce.call(() { debounce.call(() {
@ -303,30 +306,38 @@ class _IconPickerState extends State<IconPicker> {
icon: icon, icon: icon,
mutex: mutex, mutex: mutex,
onSelectedColor: (context, color) { onSelectedColor: (context, color) {
String groupName = iconGroup.name;
if (groupName == _kRecentIconGroupName) {
groupName = getGroupName(index);
}
widget.onSelectedIcon( widget.onSelectedIcon(
IconsData( IconsData(
iconGroup.name, groupName,
icon.content, icon.content,
icon.name, icon.name,
color, color,
), ),
); );
RecentIcons.putIcon(icon); RecentIcons.putIcon(RecentIcon(icon, groupName));
PopoverContainer.of(context).close(); PopoverContainer.of(context).close();
}, },
) )
: _IconNoBackground( : _IconNoBackground(
icon: icon, icon: icon,
onSelectedIcon: () { onSelectedIcon: () {
String groupName = iconGroup.name;
if (groupName == _kRecentIconGroupName) {
groupName = getGroupName(index);
}
widget.onSelectedIcon( widget.onSelectedIcon(
IconsData( IconsData(
iconGroup.name, groupName,
icon.content, icon.content,
icon.name, icon.name,
null, null,
), ),
); );
RecentIcons.putIcon(icon); RecentIcons.putIcon(RecentIcon(icon, groupName));
}, },
); );
}, },
@ -341,6 +352,16 @@ class _IconPickerState extends State<IconPicker> {
}, },
); );
} }
String getGroupName(int index) {
final recentIcons = RecentIcons.getIconsSync();
try {
return recentIcons[index].groupName;
} catch (e) {
Log.error('getGroupName with index: $index error', e);
return '';
}
}
} }
class _IconNoBackground extends StatelessWidget { class _IconNoBackground extends StatelessWidget {
@ -392,12 +413,20 @@ class _Icon extends StatefulWidget {
class _IconState extends State<_Icon> { class _IconState extends State<_Icon> {
final PopoverController _popoverController = PopoverController(); final PopoverController _popoverController = PopoverController();
@override
void dispose() {
super.dispose();
_popoverController.close();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppFlowyPopover( return AppFlowyPopover(
direction: PopoverDirection.bottomWithCenterAligned, direction: PopoverDirection.bottomWithCenterAligned,
controller: _popoverController,
offset: const Offset(0, 6), offset: const Offset(0, 6),
mutex: widget.mutex, mutex: widget.mutex,
clickHandler: PopoverClickHandler.gestureDetector,
child: _IconNoBackground( child: _IconNoBackground(
icon: widget.icon, icon: widget.icon,
onSelectedIcon: () => _popoverController.show(), onSelectedIcon: () => _popoverController.show(),

View File

@ -22,13 +22,10 @@ class RecentIcons {
await _put(FlowyIconType.emoji, id); await _put(FlowyIconType.emoji, id);
} }
static Future<void> putIcon(Icon icon) async { static Future<void> putIcon(RecentIcon icon) async {
await _put( await _put(
FlowyIconType.icon, FlowyIconType.icon,
jsonEncode( jsonEncode(icon.toJson()),
Icon(name: icon.name, keywords: icon.keywords, content: icon.content)
.toJson(),
),
); );
} }
@ -37,12 +34,18 @@ class RecentIcons {
return _dataMap[FlowyIconType.emoji.name] ?? []; return _dataMap[FlowyIconType.emoji.name] ?? [];
} }
static Future<List<Icon>> getIcons() async { static Future<List<RecentIcon>> getIcons() async {
await _load(); await _load();
return getIconsSync();
}
static List<RecentIcon> getIconsSync() {
final iconList = _dataMap[FlowyIconType.icon.name] ?? []; final iconList = _dataMap[FlowyIconType.icon.name] ?? [];
try { try {
return iconList return iconList
.map((e) => Icon.fromJson(jsonDecode(e) as Map<String, dynamic>)) .map(
(e) => RecentIcon.fromJson(jsonDecode(e) as Map<String, dynamic>),
)
.toList(); .toList();
} catch (e) { } catch (e) {
Log.error('RecentIcons getIcons with :$iconList', e); Log.error('RecentIcons getIcons with :$iconList', e);

View File

@ -46,16 +46,17 @@ void main() {
}); });
test('putIcons', () async { test('putIcons', () async {
List<Icon> icons = await RecentIcons.getIcons(); List<RecentIcon> icons = await RecentIcons.getIcons();
assert(icons.isEmpty); assert(icons.isEmpty);
await loadIconGroups(); await loadIconGroups();
final groups = kIconGroups!; final groups = kIconGroups!;
final List<Icon> localIcons = []; final List<RecentIcon> localIcons = [];
for (final e in groups) { for (final e in groups) {
localIcons.addAll(e.icons); localIcons.addAll(e.icons.map((e) => RecentIcon(e, e.name)).toList());
} }
bool equalIcon(Icon a, Icon b) => bool equalIcon(RecentIcon a, RecentIcon b) =>
a.groupName == b.groupName &&
a.name == b.name && a.name == b.name &&
a.keywords.equals(b.keywords) && a.keywords.equals(b.keywords) &&
a.content == b.content; a.content == b.content;