Skip to content

Commit

Permalink
Merge pull request #2623 from nextcloud/refactor/neon_framework/custo…
Browse files Browse the repository at this point in the history
…m-emoji-picker
  • Loading branch information
provokateurin authored Nov 20, 2024
2 parents e7d35f9 + 8317e47 commit a26468d
Show file tree
Hide file tree
Showing 20 changed files with 25,113 additions and 72 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
[submodule "external/nextcloud-tables"]
path = external/nextcloud-tables
url = https://github.com/nextcloud/tables
[submodule "external/emoji-metadata"]
path = external/emoji-metadata
url = https://github.com/googlefonts/emoji-metadata
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"**/CHANGELOG.md",
"external",
"packages/dynamite/example/lib",
"packages/neon_framework/lib/src/utils/emojis.dart",
"packages/neon_framework/example/web/sqflite_sw.js",
"packages/neon_framework/example/web/sqlite3.wasm",
"packages/neon_lints/lib",
Expand Down
1 change: 1 addition & 0 deletions external/emoji-metadata
Submodule emoji-metadata added at 3544a2
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "generated_plugin_registrant.h"

#include <dynamic_color/dynamic_color_plugin.h>
#include <emoji_picker_flutter/emoji_picker_flutter_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
Expand All @@ -17,9 +16,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) emoji_picker_flutter_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "EmojiPickerFlutterPlugin");
emoji_picker_flutter_plugin_register_with_registrar(emoji_picker_flutter_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
emoji_picker_flutter
file_selector_linux
screen_retriever_linux
url_launcher_linux
Expand Down
8 changes: 0 additions & 8 deletions packages/neon_framework/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,6 @@ packages:
relative: true
source: path
version: "0.5.0+1"
emoji_picker_flutter:
dependency: transitive
description:
name: emoji_picker_flutter
sha256: "08567e6f914d36c32091a96cf2f51d2558c47aa2bd47a590dc4f50e42e0965f6"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
equatable:
dependency: transitive
description:
Expand Down
225 changes: 225 additions & 0 deletions packages/neon_framework/generate_emojis.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import 'dart:convert';

import 'package:code_builder/code_builder.dart';
import 'package:collection/collection.dart';
import 'package:dart_style/dart_style.dart';
import 'package:string_normalizer/string_normalizer.dart';
import 'package:universal_io/io.dart';

/// Android 14 only supports Unicode 15.0, so we use that for now, even though newer versions are available:
/// https://developer.android.com/guide/topics/resources/internationalization#versioning-nougat
const unicodeVersion = '15_0';

void main() {
final formatter = DartFormatter(pageWidth: 120);
final emitter = DartEmitter(
orderDirectives: true,
useNullSafetySyntax: true,
);

final inputFile = File('../../external/emoji-metadata/emoji_${unicodeVersion}_ordering.json');
final input = json.decode(inputFile.readAsStringSync());

final library = Library((b) {
b.docs.add('// GENERATED CODE - DO NOT MODIFY BY HAND');

b.body.add(
Class(
(b) => b
..name = 'Emoji'
..docs.add('/// Holds the metadata of a Unicode emoji.')
..constructors.add(
Constructor(
(b) => b
..constant = true
..docs.add('/// Creates a new [Emoji].')
..optionalParameters.addAll([
Parameter(
(b) => b
..name = 'base'
..named = true
..toThis = true
..required = true,
),
Parameter(
(b) => b
..name = 'alternates'
..named = true
..toThis = true
..required = true,
),
Parameter(
(b) => b
..name = 'emoticons'
..named = true
..toThis = true
..required = true,
),
Parameter(
(b) => b
..name = 'shortcodes'
..named = true
..toThis = true
..required = true,
),
Parameter(
(b) => b
..name = 'animated'
..named = true
..toThis = true
..required = true,
),
]),
),
)
..fields.addAll([
Field(
(b) => b
..name = 'base'
..type = refer('String')
..modifier = FieldModifier.final$
..docs.add('/// The base emoji symbol.'),
),
Field(
(b) => b
..name = 'alternates'
..type = refer('List<String>')
..modifier = FieldModifier.final$
..docs.add('/// The associated alternate emoji symbols.'),
),
Field(
(b) => b
..name = 'emoticons'
..type = refer('List<String>')
..modifier = FieldModifier.final$
..docs.add('/// The associated emoticons.'),
),
Field(
(b) => b
..name = 'shortcodes'
..type = refer('List<String>')
..modifier = FieldModifier.final$
..docs.add('/// The associated short codes.'),
),
Field(
(b) => b
..name = 'animated'
..type = refer('bool')
..modifier = FieldModifier.final$
..docs.add('/// Whether the emoji can be animated.'),
),
]),
),
);

final names = <String, String>{};
const groupIds = <String, String>{
'Smileys and emotions': 'smileysAndEmotions',
'People': 'people',
'Animals and nature': 'animalsAndNature',
'Food and drink': 'foodAndDrink',
'Travel and places': 'travelAndPlaces',
'Activities and events': 'activitiesAndEvents',
'Objects': 'objects',
'Symbols': 'symbols',
'Flags': 'flags',
};
final emojiGroups = <String, List<String>>{};

for (final group in (input as List).map((t) => t as Map<String, dynamic>)) {
final groupName = group['group'] as String;
final groupId = groupIds[groupName]!;

for (final emoji in (group['emoji'] as List).map((t) => t as Map<String, dynamic>)) {
final base = String.fromCharCodes((emoji['base'] as List).cast<int>());
final alternates = (emoji['alternates'] as List)
.map((alternate) => String.fromCharCodes((alternate as List).cast<int>()))
.toList();
final emoticons = (emoji['emoticons'] as List).cast<String>();
final shortcodes = (emoji['shortcodes'] as List).cast<String>();
final animated = emoji['animated'] as bool;

var name = '';
for (final shortcode in shortcodes) {
name = shortcode.substring(1, shortcode.length - 1).toLowerCase();
name = name
.split('-')
.map(
(t) => switch (t) {
'1' => 'one',
'2' => 'two',
'3' => 'three',
'4' => 'four',
'5' => 'five',
'6' => 'six',
'7' => 'seven',
'8' => 'eight',
'9' => 'nine',
_ => t,
},
)
.mapIndexed((i, t) => i == 0 ? t : '${t.substring(0, 1).toUpperCase()}${t.substring(1)}')
.join();
name = StringNormalizer.normalize(name);
name = name.replaceAll(RegExp('[^a-z]', caseSensitive: false), '');
if (name.isNotEmpty) {
break;
}
}

if (name == 'new') {
name = r'$new';
}

emojiGroups[groupId] ??= [];
emojiGroups[groupId]!.add(name);

// Some emojis are in multiple groups
if (names.containsKey(name)) {
if (names[name] != base) {
throw Exception('Conflicting name $name for different emojis: $base ${names[name]}');
}
continue;
}
names[name] = base;

b.body.add(
Code('''
/// The $base emoji.
const $name = Emoji(
base: '$base',
alternates: [${alternates.isNotEmpty ? "'${alternates.join("', '")}'," : ''}],
emoticons: [${emoticons.isNotEmpty ? "'${emoticons.join("', '").replaceAll("'", r"\'").replaceAll(r'$', r'\$')}'," : ''}],
shortcodes: ['${shortcodes.join("', '")}',],
animated: $animated,
);
'''),
);
}
}

for (final groupId in emojiGroups.keys) {
final emojis = emojiGroups[groupId]!;

b.body.add(
Code('''
/// The "${groupIds.entries.firstWhere((e) => e.value == groupId).key}" emojis.
const ${groupId}Group = [${emojis.join(', ')},];
'''),
);
}

b.body.add(
Code('''
/// All emojis.
const all = [${emojiGroups.values.expand((t) => [...t]).join(', ')},];
'''),
);
});

final output = library.accept(emitter).toString();
File('lib/src/utils/emojis.dart').writeAsStringSync(formatter.format(output));
}
11 changes: 10 additions & 1 deletion packages/neon_framework/lib/l10n/en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -294,5 +294,14 @@
"userStatusClearAtThisWeek": "This week",
"userStatusActionClear": "Clear status",
"userStatusStatusMessage": "Status message",
"userStatusOnlineStatus": "Online status"
"userStatusOnlineStatus": "Online status",
"emojisCategorySmileysAndEmotions": "Smileys and emotions",
"emojisCategoryPeople": "People",
"emojisCategoryAnimalsAndNature": "Animals and nature",
"emojisCategoryFoodAndDrink": "Food and drink",
"emojisCategoryTravelAndPlaces": "Travel and places",
"emojisCategoryActivitiesAndEvents": "Activities and events",
"emojisCategoryObjects": "Objects",
"emojisCategorySymbols": "Symbols",
"emojisCategoryFlags": "Flags"
}
54 changes: 54 additions & 0 deletions packages/neon_framework/lib/l10n/localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,60 @@ abstract class NeonLocalizations {
/// In en, this message translates to:
/// **'Online status'**
String get userStatusOnlineStatus;

/// No description provided for @emojisCategorySmileysAndEmotions.
///
/// In en, this message translates to:
/// **'Smileys and emotions'**
String get emojisCategorySmileysAndEmotions;

/// No description provided for @emojisCategoryPeople.
///
/// In en, this message translates to:
/// **'People'**
String get emojisCategoryPeople;

/// No description provided for @emojisCategoryAnimalsAndNature.
///
/// In en, this message translates to:
/// **'Animals and nature'**
String get emojisCategoryAnimalsAndNature;

/// No description provided for @emojisCategoryFoodAndDrink.
///
/// In en, this message translates to:
/// **'Food and drink'**
String get emojisCategoryFoodAndDrink;

/// No description provided for @emojisCategoryTravelAndPlaces.
///
/// In en, this message translates to:
/// **'Travel and places'**
String get emojisCategoryTravelAndPlaces;

/// No description provided for @emojisCategoryActivitiesAndEvents.
///
/// In en, this message translates to:
/// **'Activities and events'**
String get emojisCategoryActivitiesAndEvents;

/// No description provided for @emojisCategoryObjects.
///
/// In en, this message translates to:
/// **'Objects'**
String get emojisCategoryObjects;

/// No description provided for @emojisCategorySymbols.
///
/// In en, this message translates to:
/// **'Symbols'**
String get emojisCategorySymbols;

/// No description provided for @emojisCategoryFlags.
///
/// In en, this message translates to:
/// **'Flags'**
String get emojisCategoryFlags;
}

class _NeonLocalizationsDelegate extends LocalizationsDelegate<NeonLocalizations> {
Expand Down
27 changes: 27 additions & 0 deletions packages/neon_framework/lib/l10n/localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -507,4 +507,31 @@ class NeonLocalizationsEn extends NeonLocalizations {

@override
String get userStatusOnlineStatus => 'Online status';

@override
String get emojisCategorySmileysAndEmotions => 'Smileys and emotions';

@override
String get emojisCategoryPeople => 'People';

@override
String get emojisCategoryAnimalsAndNature => 'Animals and nature';

@override
String get emojisCategoryFoodAndDrink => 'Food and drink';

@override
String get emojisCategoryTravelAndPlaces => 'Travel and places';

@override
String get emojisCategoryActivitiesAndEvents => 'Activities and events';

@override
String get emojisCategoryObjects => 'Objects';

@override
String get emojisCategorySymbols => 'Symbols';

@override
String get emojisCategoryFlags => 'Flags';
}
Loading

0 comments on commit a26468d

Please sign in to comment.